package com.wondertek.onvif.service;

import cn.hutool.core.util.StrUtil;
import com.wondertek.onvif.entity.OnvifDevice;
import com.wondertek.onvif.service.auth.AuthenticationManager;
import com.wondertek.onvif.service.auth.AuthenticationStrategy;
import com.wondertek.onvif.service.auth.WsSecurityAuthStrategy;
import com.wondertek.onvif.service.connection.HikvisionConnectionManager;
import com.wondertek.onvif.config.DeviceConfigManager;
import com.wondertek.onvif.connection.ConnectionPoolManager;
import com.wondertek.onvif.retry.RetryManager;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import javax.xml.namespace.QName;
import javax.xml.soap.*;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.Comparator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * ONVIF客户端服务
 * 实现与ONVIF设备的SOAP通信
 * 
 * @author wondertek
 * @version 1.0.0
 */
@Slf4j
@Service
public class OnvifClientService {

    private static final Logger log = LoggerFactory.getLogger(OnvifClientService.class);
    
    private static final int CONNECTION_TIMEOUT = 5000; // 优化连接超时为5秒
    private static final int READ_TIMEOUT = 5000; // 优化读取超时为5秒
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private DeviceConfigManager deviceConfigManager;
    
    @Autowired
    private ConnectionPoolManager connectionPoolManager;
    
    @Autowired
    private RetryManager retryManager;
    
    @Autowired
    private HikvisionConnectionManager hikvisionConnectionManager;
    
    @Autowired
    private OnvifCompatibilityService compatibilityService;
    
    /**
     * 测试设备连接
     * 
     * @param device 设备信息
     * @return 连接是否成功
     */
    public boolean testConnection(OnvifDevice device) {
        long startTime = System.currentTimeMillis();
        String deviceInfo = String.format("%s:%s", device.getIpAddress(), device.getPort());
        
        try {
            log.info("=== 开始设备连接测试 ===");
            log.info("设备信息: {}", deviceInfo);
            log.info("用户名: {}", device.getUsername());
            log.info("测试开始时间: {}", new java.util.Date());
            
            // 1. 获取设备服务地址
            String serviceUrl = getDeviceServiceUrl(device);
            log.info("步骤1: 获取设备服务地址 - {}", serviceUrl);
            
            // 2. 构建SOAP请求
            String soapRequest = buildGetCapabilitiesRequest();
            log.info("步骤2: 构建GetCapabilities请求，请求长度: {} 字符", soapRequest.length());
            log.debug("SOAP请求内容: {}", soapRequest);
            
            // 3. 发送SOAP请求
            log.info("步骤3: 发送SOAP请求到 {}", serviceUrl);
            long requestStartTime = System.currentTimeMillis();
            
            String response = sendSoapRequest(serviceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            long requestEndTime = System.currentTimeMillis();
            long requestDuration = requestEndTime - requestStartTime;
            log.info("SOAP请求完成，耗时: {}ms", requestDuration);
            
            // 4. 分析响应
            boolean isConnected = false;
            if (StrUtil.isEmpty(response)) {
                log.error("步骤4: 响应分析失败 - 响应为空");
                log.error("可能原因: 1) 网络连接问题 2) 设备未响应 3) 认证失败但未返回错误信息");
            } else {
                log.info("步骤4: 收到响应，长度: {} 字符", response.length());
                log.debug("响应内容前500字符: {}", response.substring(0, Math.min(response.length(), 500)));
                
                // 更全面的成功判断条件
                boolean hasCapabilities = response.contains("Capabilities");
                boolean hasGetCapabilitiesResponse = response.contains("GetCapabilitiesResponse");
                boolean hasDeviceService = response.contains("Device") && response.contains("XAddr");
                boolean hasMediaService = response.contains("Media") && response.contains("XAddr");
                boolean hasValidXmlStructure = response.contains("<?xml") || response.contains("<soap:") || response.contains("<env:");
                
                isConnected = hasCapabilities || hasGetCapabilitiesResponse || (hasValidXmlStructure && (hasDeviceService || hasMediaService));
                
                if (isConnected) {
                    log.info("连接测试成功 - 检测到有效ONVIF响应");
                    if (hasCapabilities) log.info("✓ 响应包含Capabilities");
                    if (hasGetCapabilitiesResponse) log.info("✓ 响应包含GetCapabilitiesResponse");
                    if (hasDeviceService) log.info("✓ 响应包含设备服务信息");
                    if (hasMediaService) log.info("✓ 响应包含媒体服务信息");
                } else {
                    log.warn("连接测试失败 - 响应不符合ONVIF标准");
                    
                    // 详细分析响应内容
                    if (response.contains("<soap:Fault>") || response.contains("<env:Fault>")) {
                        log.error("✗ 响应包含SOAP错误");
                        // 提取具体错误信息
                        String faultString = extractValue(response, "<faultstring>(.*?)</faultstring>", "未知SOAP错误");
                        log.error("SOAP错误详情: {}", faultString);
                    }
                    if (response.contains("401") || response.contains("Unauthorized")) {
                        log.error("✗ 认证失败 - HTTP 401 Unauthorized");
                        log.error("建议: 1) 检查用户名密码是否正确 2) 检查设备认证方式设置");
                    }
                    if (response.contains("404") || response.contains("Not Found")) {
                        log.error("✗ 服务路径不存在 - HTTP 404 Not Found");
                        log.error("建议: 1) 检查ONVIF服务地址是否正确 2) 尝试其他常见ONVIF路径");
                    }
                    if (response.contains("403") || response.contains("Forbidden")) {
                        log.error("✗ 访问被禁止 - HTTP 403 Forbidden");
                        log.error("建议: 1) 检查用户权限设置 2) 检查设备访问控制列表");
                    }
                    if (response.contains("500") || response.contains("Internal Server Error")) {
                        log.error("✗ 设备内部错误 - HTTP 500 Internal Server Error");
                        log.error("建议: 1) 设备可能过载，稍后重试 2) 检查设备固件版本");
                    }
                    
                    // 检查是否是HTML响应（可能是Web管理界面）
                    if (response.contains("<html>") || response.contains("<HTML>")) {
                        log.error("✗ 收到HTML响应，可能访问了Web管理界面而非ONVIF服务");
                        log.error("建议: 1) 检查端口号是否正确 2) 确认设备ONVIF服务已启用");
                    }
                    
                    // 检查响应是否为空或过短
                    if (response.length() < 50) {
                        log.error("✗ 响应内容过短，可能不是有效的ONVIF响应");
                        log.error("响应内容: {}", response);
                    }
                }
            }
            
            long totalTime = System.currentTimeMillis() - startTime;
            log.info("=== 设备连接测试完成 ===");
            log.info("测试结果: {}", isConnected ? "成功" : "失败");
            log.info("总耗时: {}ms", totalTime);
            log.info("设备: {}", deviceInfo);
            
            return isConnected;
            
        } catch (Exception e) {
            long totalTime = System.currentTimeMillis() - startTime;
            
            // 检查是否是ActionNotSupported错误，如果是则视为部分成功
            if (e instanceof IOException && e.getMessage() != null) {
                String errorMessage = e.getMessage();
                
                // 检查是否是ActionNotSupported错误
                if (compatibilityService.isActionNotSupportedError(errorMessage)) {
                    log.warn("=== 设备连接测试 - 检测到ActionNotSupported错误 ===");
                    log.warn("设备: {}", deviceInfo);
                    log.warn("错误信息: {}", errorMessage);
                    log.warn("兼容性状态: 部分兼容 - 设备可连接但不支持GetCapabilities操作");
                    log.warn("总耗时: {}ms", totalTime);
                    
                    // 生成兼容性建议
                    String advice = compatibilityService.generateCompatibilityAdvice(errorMessage, 400);
                    log.info("兼容性建议:\n{}", advice);
                    
                    // 对于ActionNotSupported错误，返回true表示设备是可连接的
                    return true;
                }
            }
            
            log.error("=== 设备连接测试异常 ===");
            log.error("设备: {}", deviceInfo);
            log.error("异常类型: {}", e.getClass().getSimpleName());
            log.error("异常信息: {}", e.getMessage());
            log.error("总耗时: {}ms", totalTime);
            log.error("异常堆栈:", e);
            
            // 提供详细的错误分析
            analyzeConnectionError(e, device);
            
            return false;
        }
    }

    /**
     * 优化的媒体服务地址获取方法（带重试机制）
     */
    private String getMediaServiceUrlWithRetry(OnvifDevice device) {
        // 检测是否为海康设备
        boolean isHikvision = isHikvisionDevice(device);
        
        // 首先尝试标准方法，海康设备增加重试
        int retries = isHikvision ? 3 : 1;
        String mediaServiceUrl = null;
        
        for (int attempt = 1; attempt <= retries; attempt++) {
            try {
                if (attempt > 1) {
                    log.info("重试获取媒体服务地址 {}/{}: {}:{}", attempt, retries, device.getIpAddress(), device.getPort());
                    // 海康设备重试前清理连接状态
                    if (isHikvision) {
                        System.clearProperty("http.keepAlive");
                        System.clearProperty("http.maxConnections");
                        Thread.sleep(1000 * attempt); // 递增等待时间
                    }
                }
                
                mediaServiceUrl = getMediaServiceUrl(device);
                if (StrUtil.isNotEmpty(mediaServiceUrl)) {
                    log.info("成功获取媒体服务地址 (尝试 {}): {}", attempt, mediaServiceUrl);
                    return mediaServiceUrl;
                }
            } catch (Exception e) {
                log.warn("获取媒体服务地址异常 (尝试 {}): {}:{} - {}", 
                    attempt, device.getIpAddress(), device.getPort(), e.getMessage());
                if (attempt < retries && isHikvision && 
                    (e.getMessage() != null && 
                     (e.getMessage().contains("Unexpected end of file") ||
                      e.getMessage().contains("Connection reset") ||
                      e.getMessage().contains("timeout")))) {
                    continue;
                }
            }
        }
        
        log.warn("标准方法获取媒体服务地址失败，尝试备用路径: {}:{}", device.getIpAddress(), device.getPort());
        
        // 尝试使用备用媒体服务路径，海康设备优先使用海康特有路径
        String[] alternativePaths;
        if (isHikvision) {
            alternativePaths = new String[]{
                "/onvif/Media",           // 海康常用路径
                "/onvif/media",           // 小写变体
                "/onvif/Media2",          // Media2服务
                "/ISAPI/Media",           // 海康ISAPI接口
                "/onvif/media_service",   // 完整服务名
                "/Media",                 // 简化路径
                "/media",                 // 简化小写路径
                "/onvif/device_service",  // 设备服务
                "/ONVIF/Media",           // 大写路径
                "/ONVIF/media_service"
            };
        } else {
            alternativePaths = new String[]{
                "/onvif/Media",
                "/onvif/media_service", 
                "/onvif/Media2",
                "/Media",
                "/media",
                "/onvif/device_service",  // 有些设备媒体服务和设备服务是同一个
                "/ONVIF/Media",           // 大写路径
                "/ONVIF/media_service"
            };
        }
        
        for (String path : alternativePaths) {
            String alternativeUrl = String.format("http://%s:%d%s", 
                device.getIpAddress(), device.getPort(), path);
            log.info("尝试备用媒体服务地址: {}", alternativeUrl);
            
            try {
                if (testMediaServiceUrl(alternativeUrl, device)) {
                    log.info("备用媒体服务地址有效: {}", alternativeUrl);
                    return alternativeUrl;
                }
            } catch (Exception e) {
                log.debug("备用媒体服务地址无效: {} - {}", alternativeUrl, e.getMessage());
            }
        }
        
        log.error("所有媒体服务地址都无法访问，设备: {}:{}", device.getIpAddress(), device.getPort());
        return "";
    }





    /**
     * 优化的SOAP请求方法
     */
    private String sendSoapRequestWithOptimization(String serviceUrl, String soapRequest, OnvifDevice device) throws IOException {
        // 检测设备类型
        String deviceType = detectDeviceTypeByDevice(device);
        DeviceConfigManager.DeviceConfig config = deviceConfigManager.getDeviceConfig(deviceType);
        
        log.info("使用优化请求方法，设备类型: {}", deviceType != null ? deviceType : "默认");
        
        // 对海康设备使用专门的连接处理逻辑
        if ("hikvision".equals(deviceType)) {
            return sendSoapRequestForHikvision(serviceUrl, soapRequest, device.getUsername(), device.getPassword(), config);
        }
        
        // 其他设备使用标准方法，但增加重试机制
        int maxRetries = 3;
        IOException lastException = null;
        
        for (int i = 0; i < maxRetries; i++) {
            try {
                if (i > 0) {
                    log.info("SOAP请求重试 {}/{}: {}", i + 1, maxRetries, serviceUrl);
                    // 重试前短暂等待
                    Thread.sleep(500 * (i + 1));
                }
                String response = sendSoapRequest(serviceUrl, soapRequest, device.getUsername(), device.getPassword());
                if (StrUtil.isNotEmpty(response)) {
                    return response;
                }
            } catch (IOException e) {
                lastException = e;
                log.warn("SOAP请求失败，第{}次重试: {} - {}", i + 1, serviceUrl, e.getMessage());
                
                // 智能判断是否应该重试
                boolean shouldRetry = false;
                int waitTime = 500 * (i + 1);
                
                if (e.getMessage() != null) {
                    String errorMsg = e.getMessage().toLowerCase();
                    if (errorMsg.contains("unexpected end of file") ||
                        errorMsg.contains("connection reset") ||
                        errorMsg.contains("connection timed out") ||
                        errorMsg.contains("read timed out") ||
                        errorMsg.contains("socketexception") ||
                        errorMsg.contains("connect in progress")) {
                        shouldRetry = true;
                        waitTime = 1000 * (i + 1); // 网络错误需要更长等待时间
                        log.info("检测到网络连接问题，将在{}ms后重试", waitTime);
                    } else if (errorMsg.contains("401") || errorMsg.contains("unauthorized")) {
                        log.warn("认证失败，跳过重试: {}", e.getMessage());
                        break; // 认证错误不重试
                    }
                }
                
                // 如果是最后一次重试或不应该重试，直接退出
                if (i == maxRetries - 1 || !shouldRetry) {
                    break;
                }
                
                // 等待后重试
                try {
                    Thread.sleep(waitTime);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    break;
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new IOException("请求被中断", e);
            }
        }
        
        if (lastException != null) {
            throw lastException;
        }
        
        throw new IOException("SOAP请求失败，已重试" + maxRetries + "次: " + serviceUrl);
    }

    /**
     * 优化的流地址获取方法
     */
    private String getStreamUriWithOptimization(OnvifDevice device, String mediaServiceUrl, String profileToken) {
        // 尝试多种方法获取RTSP流地址
        String[] methods = {"standard", "alternative", "direct"};
        
        for (String method : methods) {
            try {
                String rtspUrl = null;
                
                switch (method) {
                    case "standard":
                        rtspUrl = getStreamUriStandard(device, mediaServiceUrl, profileToken);
                        break;
                    case "alternative":
                        rtspUrl = getStreamUriAlternative(device, profileToken);
                        break;
                    case "direct":
                        rtspUrl = generateDirectRtspUrl(device, profileToken);
                        break;
                }
                
                if (StrUtil.isNotEmpty(rtspUrl) && rtspUrl.startsWith("rtsp://")) {
                    // 隐藏RTSP URL中的密码信息
                    String maskedRtspUrl = rtspUrl.replaceAll("://[^@]*@", "://***:***@");
                    log.info("使用{}方法成功获取RTSP地址: {}", method, maskedRtspUrl);
                    return rtspUrl;
                }
                
            } catch (Exception e) {
                log.warn("{}方法获取流地址失败: {}", method, e.getMessage());
            }
        }
        
        log.warn("所有方法都无法获取RTSP地址，profile: {}", profileToken);
        return "";
    }

    /**
     * 标准的流地址获取方法
     */
    private String getStreamUriStandard(OnvifDevice device, String mediaServiceUrl, String profileToken) {
        try {
            log.debug("开始获取流地址，Profile Token: {}", profileToken);
            
            String soapRequest = buildGetStreamUriRequest(profileToken);
            
            // 使用优化的请求方法
            String response = sendSoapRequestWithOptimization(mediaServiceUrl, soapRequest, device);
            
            if (StrUtil.isNotEmpty(response)) {
                String rtspUrl = extractValue(response, "<tt:Uri>", "</tt:Uri>");
                if (StrUtil.isEmpty(rtspUrl)) {
                    rtspUrl = extractValue(response, "<Uri>", "</Uri>");
                }
                
                if (StrUtil.isNotEmpty(rtspUrl)) {
                    log.debug("标准方法成功获取流地址: {}", rtspUrl);
                    return rtspUrl;
                } else {
                    log.warn("响应中未找到流地址，Profile Token: {}", profileToken);
                }
            }
        } catch (Exception e) {
            log.warn("标准方法获取流地址失败，Profile Token: {} - {}", profileToken, e.getMessage());
        }
        
        return null;
    }
    
    /**
     * 备用的流地址获取方法
     */
    private String getStreamUriAlternative(OnvifDevice device, String profileToken) {
        try {
            // 尝试使用不同的媒体服务路径
            String[] mediaPaths = {
                "/onvif/Media",
                "/onvif/media",
                "/onvif/Media2",
                "/Media",
                "/media"
            };
            
            for (String path : mediaPaths) {
                String mediaUrl = String.format("http://%s:%d%s", 
                    device.getIpAddress(), device.getPort(), path);
                
                try {
                    String rtspUrl = getStreamUriStandard(device, mediaUrl, profileToken);
                    if (StrUtil.isNotEmpty(rtspUrl)) {
                        log.info("备用路径成功: {}", path);
                        return rtspUrl;
                    }
                } catch (Exception e) {
                    log.debug("备用媒体路径失败: {} - {}", path, e.getMessage());
                }
            }
        } catch (Exception e) {
            log.warn("备用流地址获取方法失败: {}", e.getMessage());
        }
        
        return null;
    }
    
    /**
     * 直接生成RTSP地址（基于常见模式）
     */
    private String generateDirectRtspUrl(OnvifDevice device, String profileToken) {
        try {
            // 常见的RTSP地址模式
            String[] patterns = {
                "rtsp://%s:%s@%s:554/Streaming/Channels/%s", // 海康模式
                "rtsp://%s:%s@%s:554/cam/realmonitor?channel=%s&subtype=0", // 大华模式
                "rtsp://%s:%s@%s:554/onvif/%s", // 通用ONVIF模式
                "rtsp://%s:%s@%s:554/profile_%s", // 标准Profile模式
                "rtsp://%s:%s@%s:554/stream_%s", // 流模式
                "rtsp://%s:%s@%s:554/%s" // 简单模式
            };
            
            String username = device.getUsername();
            String password = device.getPassword();
            String ip = device.getIpAddress();
            
            // 尝试从profileToken中提取通道号
            String channelId = extractChannelFromToken(profileToken);
            
            for (String pattern : patterns) {
                String rtspUrl = String.format(pattern, username, password, ip, channelId);
                log.debug("尝试直接生成RTSP地址: {}", rtspUrl.replaceAll(password, "***"));
                
                // 这里只是生成地址，实际验证需要客户端测试
                return rtspUrl;
            }
            
        } catch (Exception e) {
            log.warn("直接生成RTSP地址失败: {}", e.getMessage());
        }
        
        return null;
    }
    
    /**
     * 从Profile Token中提取通道号
     */
    private String extractChannelFromToken(String profileToken) {
        if (StrUtil.isEmpty(profileToken)) {
            return "1";
        }
        
        // 尝试提取数字
        Pattern pattern = Pattern.compile("(\\d+)");
        Matcher matcher = pattern.matcher(profileToken);
        if (matcher.find()) {
            return matcher.group(1);
        }
        
        // 如果没有数字，返回默认值
        return "1";
    }

    /**
     * 增强的配置文件解析方法
     */
    private List<ProfileInfo> parseProfilesWithEnhancement(String response) {
        List<ProfileInfo> profiles = parseProfiles(response);
        
        // 如果标准解析失败，尝试更宽松的解析
        if (profiles.isEmpty() && response.toLowerCase().contains("profile")) {
            log.info("标准解析失败，尝试宽松解析模式");
            profiles = parseProfilesLoose(response);
        }
        
        return profiles;
    }

    /**
     * 宽松的配置文件解析方法
     */
    private List<ProfileInfo> parseProfilesLoose(String response) {
        List<ProfileInfo> profiles = new ArrayList<>();
        
        // 使用更宽松的正则表达式
        String[] loosePatterns = {
            "(?i)token\s*=\s*[\"']([^\"']*)[\"'][^>]*>.*?profile",
            "(?i)profile[^>]*token\s*=\s*[\"']([^\"']*)[\"']",
            "(?i)<[^>]*profile[^>]*>.*?token[^>]*>([^<]*)<",
            "(?i)token[^>]*>([^<]*)</.*?profile"
        };
        
        for (String patternStr : loosePatterns) {
            Pattern pattern = Pattern.compile(patternStr, Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
            Matcher matcher = pattern.matcher(response);
            
            while (matcher.find()) {
                String token = matcher.group(1).trim();
                if (StrUtil.isNotEmpty(token)) {
                    // 检查是否已经解析过这个token
                    boolean alreadyExists = profiles.stream()
                        .anyMatch(p -> token.equals(p.getToken()));
                    if (!alreadyExists) {
                        ProfileInfo profile = new ProfileInfo();
                        profile.setToken(token);
                        profile.setName("Profile_" + token);
                        profiles.add(profile);
                        log.info("宽松解析找到Profile Token: {}", token);
                    }
                }
            }
            
            if (!profiles.isEmpty()) {
                break;
            }
        }
        
        return profiles;
    }

    /**
     * 备用Profile解析方法
     * 当标准解析和宽松解析都失败时使用
     * 
     * @param response SOAP响应
     * @param device 设备信息
     * @return Profile列表
     */
    private List<ProfileInfo> parseProfilesWithFallback(String response, OnvifDevice device) {
        List<ProfileInfo> profiles = new ArrayList<>();
        
        try {
            log.info("开始备用Profile解析: {}:{}", device.getIpAddress(), device.getPort());
            
            // 方法1: 使用更激进的正则表达式
            String[] aggressivePatterns = {
                "(?i)token\s*=\s*[\"']([^\"']+)[\"'][^>]*",
                "(?i)[\"']([^\"']*profile[^\"']*)[\"']",
                "(?i)>\s*([^<]*profile[^<]*)\s*<",
                "(?i)token[^>]*>([^<]+)<"
            };
            
            for (String patternStr : aggressivePatterns) {
                Pattern pattern = Pattern.compile(patternStr, Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
                Matcher matcher = pattern.matcher(response);
                
                while (matcher.find()) {
                    String token = matcher.group(1).trim();
                    if (StrUtil.isNotEmpty(token) && token.length() > 2) {
                        // 检查是否已经存在
                        boolean exists = profiles.stream().anyMatch(p -> token.equals(p.getToken()));
                        if (!exists) {
                            ProfileInfo profile = new ProfileInfo();
                            profile.setToken(token);
                            profile.setName("Profile_" + token);
                            profiles.add(profile);
                            log.debug("备用解析找到Token: {}", token);
                        }
                    }
                }
                
                if (!profiles.isEmpty()) {
                    log.info("激进正则表达式解析成功，找到 {} 个Profile", profiles.size());
                    break;
                }
            }
            
            // 方法2: 如果正则表达式失败，尝试简单的字符串搜索
            if (profiles.isEmpty()) {
                log.info("正则表达式解析失败，尝试字符串搜索方法");
                profiles = parseProfilesByStringSearch(response, device);
            }
            
            // 方法3: 如果还是失败，尝试基于设备类型的特殊解析
            if (profiles.isEmpty()) {
                log.info("字符串搜索失败，尝试设备特定解析方法");
                profiles = parseProfilesByDeviceType(response, device);
            }
            
        } catch (Exception e) {
            log.error("备用Profile解析异常: {}:{} - {}", device.getIpAddress(), device.getPort(), e.getMessage());
        }
        
        return profiles;
    }
    
    /**
     * 基于字符串搜索的Profile解析
     */
    private List<ProfileInfo> parseProfilesByStringSearch(String response, OnvifDevice device) {
        List<ProfileInfo> profiles = new ArrayList<>();
        
        try {
            // 查找所有可能的token值
            String[] searchTerms = {"token=", "Token=", "profile", "Profile"};
            
            for (String term : searchTerms) {
                int index = 0;
                while ((index = response.toLowerCase().indexOf(term.toLowerCase(), index)) != -1) {
                    // 尝试提取token值
                    String tokenCandidate = extractTokenFromPosition(response, index);
                    if (StrUtil.isNotEmpty(tokenCandidate) && tokenCandidate.length() > 2) {
                        boolean exists = profiles.stream().anyMatch(p -> tokenCandidate.equals(p.getToken()));
                        if (!exists) {
                            ProfileInfo profile = new ProfileInfo();
                            profile.setToken(tokenCandidate);
                            profile.setName("Profile_" + tokenCandidate);
                            profiles.add(profile);
                            log.debug("字符串搜索找到Token: {}", tokenCandidate);
                        }
                    }
                    index += term.length();
                }
            }
            
        } catch (Exception e) {
            log.error("字符串搜索解析异常: {}", e.getMessage());
        }
        
        return profiles;
    }
    
    /**
     * 基于设备类型的特殊解析
     */
    private List<ProfileInfo> parseProfilesByDeviceType(String response, OnvifDevice device) {
        List<ProfileInfo> profiles = new ArrayList<>();
        
        try {
            // 检查是否为海康威视设备
            boolean isHikvision = isHikvisionDevice(device);
            
            if (isHikvision) {
                log.info("检测到海康威视设备，使用海康特定解析方法");
                profiles = parseHikvisionProfiles(response);
            } else {
                log.info("使用通用设备解析方法");
                profiles = parseGenericProfiles(response);
            }
            
        } catch (Exception e) {
            log.error("设备特定解析异常: {}", e.getMessage());
        }
        
        return profiles;
    }
    
    /**
     * 海康威视设备Profile解析
     */
    private List<ProfileInfo> parseHikvisionProfiles(String response) {
        List<ProfileInfo> profiles = new ArrayList<>();
        
        // 海康设备通常使用特定的命名规则
        String[] hikvisionPatterns = {
            "(?i)profile[_\\s]*([0-9]+)",
            "(?i)channel[_\\s]*([0-9]+)",
            "(?i)stream[_\\s]*([0-9]+)"
        };
        
        for (String patternStr : hikvisionPatterns) {
            Pattern pattern = Pattern.compile(patternStr);
            Matcher matcher = pattern.matcher(response);
            
            while (matcher.find()) {
                String number = matcher.group(1);
                String token = "Profile_" + number;
                
                boolean exists = profiles.stream().anyMatch(p -> token.equals(p.getToken()));
                if (!exists) {
                    ProfileInfo profile = new ProfileInfo();
                    profile.setToken(token);
                    profile.setName("Channel_" + number);
                    profiles.add(profile);
                    log.debug("海康解析找到Profile: {}", token);
                }
            }
        }
        
        return profiles;
    }
    
    /**
     * 通用设备Profile解析
     */
    private List<ProfileInfo> parseGenericProfiles(String response) {
        List<ProfileInfo> profiles = new ArrayList<>();
        
        // 如果所有方法都失败，创建一个默认的Profile
        if (response.toLowerCase().contains("media") || response.toLowerCase().contains("stream")) {
            ProfileInfo defaultProfile = new ProfileInfo();
            defaultProfile.setToken("DefaultProfile");
            defaultProfile.setName("Default Profile");
            profiles.add(defaultProfile);
            log.info("创建默认Profile用于通用设备");
        }
        
        return profiles;
    }
    
    /**
     * 从指定位置提取token值
     */
    private String extractTokenFromPosition(String response, int position) {
        try {
            // 查找引号
            int start = -1;
            int end = -1;
            
            // 向前查找等号和引号
            for (int i = position; i < Math.min(position + 100, response.length()); i++) {
                char c = response.charAt(i);
                if (c == '"' || c == '\'') {
                    if (start == -1) {
                        start = i + 1;
                    } else {
                        end = i;
                        break;
                    }
                }
            }
            
            if (start != -1 && end != -1 && end > start) {
                return response.substring(start, end).trim();
            }
            
        } catch (Exception e) {
            log.debug("提取token异常: {}", e.getMessage());
        }
        
        return "";
    }
    
    /**
     * 检查是否为海康威视设备
     */
    private boolean isHikvisionDevice(OnvifDevice device) {
        if (device == null) return false;
        
        String manufacturer = device.getManufacturer();
        String model = device.getModel();
        String name = device.getName();
        
        return (manufacturer != null && manufacturer.toLowerCase().contains("hikvision")) ||
               (model != null && (model.toLowerCase().contains("ds-") || model.toLowerCase().contains("hikvision"))) ||
               (name != null && (name.toLowerCase().contains("hikvision") || name.toLowerCase().contains("ds-")));
    }
    
    /**
     * 为海康设备生成优化的Media服务URL列表
     */
    private List<String> generateHikvisionMediaUrls(OnvifDevice device, String primaryUrl) {
        List<String> urls = new ArrayList<>();
        urls.add(primaryUrl);
        
        String baseUrl = String.format("http://%s:%d", device.getIpAddress(), device.getPort());
        
        // 海康设备常见的Media服务路径，按优先级排序
        String[] paths = {
            "/onvif/Media",      // 标准ONVIF Media服务
            "/onvif/media",      // 小写变体
            "/onvif/Media2",     // Media2服务
            "/ISAPI/Media",      // 海康ISAPI接口
            "/onvif/media_service", // 完整服务名
            "/Media",            // 简化路径
            "/media"             // 简化小写路径
        };
        
        for (String path : paths) {
            String url = baseUrl + path;
            if (!urls.contains(url)) {
                urls.add(url);
            }
        }
        
        log.debug("海康设备生成{}个Media服务候选URL", urls.size());
        return urls;
    }

    /**
     * 分析配置文件响应
     */
    private void analyzeProfilesResponse(String response, OnvifDevice device) {
        if (response.contains("Unauthorized") || response.contains("401")) {
            log.error("认证失败，请检查用户名和密码");
        } else if (response.contains("NotFound") || response.contains("404")) {
            log.error("媒体服务路径不正确，请检查设备ONVIF配置");
        } else if (response.contains("InternalServerError") || response.contains("500")) {
            log.error("设备内部错误，可能是设备固件问题");
        } else if (response.contains("soap:Fault") || response.contains("env:Fault")) {
            log.error("SOAP错误，可能是请求格式问题或设备不支持该操作");
        } else {
            log.warn("未知响应格式，可能是设备ONVIF实现不标准");
            // 记录响应的关键信息用于调试
            if (response.length() > 100) {
                log.debug("响应开头: {}", response.substring(0, 100));
                log.debug("响应结尾: {}", response.substring(Math.max(0, response.length() - 100)));
            }
        }
    }

    /**
     * 分析连接错误
     */
    private void analyzeConnectionError(Exception e, OnvifDevice device) {
        if (e instanceof java.net.SocketTimeoutException) {
            log.error("连接超时，建议检查: 1)网络连接 2)设备是否在线 3)防火墙设置 4)超时配置");
        } else if (e instanceof java.net.ConnectException) {
            log.error("连接被拒绝，建议检查: 1)IP地址和端口 2)设备是否启用ONVIF 3)网络可达性");
        } else if (e instanceof java.io.IOException && e.getMessage() != null) {
            String message = e.getMessage();
            if (message.contains("401")) {
                log.error("认证失败，建议检查: 1)用户名和密码 2)设备认证方式 3)用户权限");
            } else if (message.contains("404")) {
                log.error("服务路径不存在，建议检查: 1)ONVIF服务地址 2)设备ONVIF版本 3)服务是否启用");
            } else if (message.contains("500")) {
                log.error("设备内部错误，建议检查: 1)设备状态 2)请求格式 3)设备日志");
            } else if (message.contains("Unexpected end of file")) {
                log.error("连接意外断开，建议检查: 1)网络稳定性 2)设备负载 3)连接池配置");
            } else if (message.contains("SocketException")) {
                log.error("Socket异常，建议检查: 1)网络连接 2)防火墙设置 3)设备网络配置");
            }
        } else {
            log.error("未知错误类型: {}", e.getClass().getSimpleName());
        }
    }

    /**
     * 获取设备信息
     * 
     * @param device 设备
     * @return 设备信息
     */
    public DeviceInfo getDeviceInfo(OnvifDevice device) {
        try {
            log.debug("获取设备信息: {}:{}", device.getIpAddress(), device.getPort());
            
            String serviceUrl = getDeviceServiceUrl(device);
            String soapRequest = buildGetDeviceInformationRequest();
            
            String response = sendSoapRequest(serviceUrl, soapRequest, device.getUsername(), device.getPassword());
            if (StrUtil.isEmpty(response)) {
                return null;
            }
            
            return parseDeviceInfo(response);
            
        } catch (Exception e) {
            log.error("获取设备信息失败: {}:{} - {}", device.getIpAddress(), device.getPort(), e.getMessage());
            return null;
        }
    }

    /**
     * 获取设备信息（无认证方式）
     * 尝试在没有用户名和密码的情况下获取设备信息
     * 
     * @param device 设备对象
     * @return 设备信息
     */
    public DeviceInfo getDeviceInfoWithoutAuth(OnvifDevice device) {
        try {
            log.debug("尝试无认证获取设备信息: {}:{}", device.getIpAddress(), device.getPort());
            
            String serviceUrl = getDeviceServiceUrl(device);
            String soapRequest = buildGetDeviceInformationRequest();
            
            // 尝试无认证请求
            String response = sendSoapRequest(serviceUrl, soapRequest, null, null);
            if (StrUtil.isNotEmpty(response)) {
                DeviceInfo deviceInfo = parseDeviceInfo(response);
                if (deviceInfo != null && StrUtil.isNotEmpty(deviceInfo.getManufacturer())) {
                    log.info("无认证方式成功获取设备信息: {}:{}", device.getIpAddress(), device.getPort());
                    return deviceInfo;
                }
            }
            
            // 如果无认证失败，尝试使用常见的默认认证信息
            String[] defaultUsers = {"admin", "root", "user"};
            String[] defaultPasswords = {"", "admin", "123456", "password", "12345"};
            
            for (String username : defaultUsers) {
                for (String password : defaultPasswords) {
                    try {
                        response = sendSoapRequest(serviceUrl, soapRequest, username, password);
                        if (StrUtil.isNotEmpty(response)) {
                            DeviceInfo deviceInfo = parseDeviceInfo(response);
                            if (deviceInfo != null && StrUtil.isNotEmpty(deviceInfo.getManufacturer())) {
                                log.info("使用默认认证成功获取设备信息: {}:{} (用户名: {})", 
                                    device.getIpAddress(), device.getPort(), username);
                                return deviceInfo;
                            }
                        }
                    } catch (Exception e) {
                        // 忽略单次尝试的异常，继续下一个组合
                        log.trace("默认认证尝试失败: {}:{} ({}:{})", 
                            device.getIpAddress(), device.getPort(), username, password);
                    }
                }
            }
            
            log.warn("无法通过任何方式获取设备信息: {}:{}", device.getIpAddress(), device.getPort());
            return null;
            
        } catch (Exception e) {
            log.error("无认证获取设备信息失败: {}:{} - {}", device.getIpAddress(), device.getPort(), e.getMessage());
            return null;
        }
    }

    /**
     * 获取设备配置文件
     * 
     * @param device 设备
     * @return 配置文件列表
     */
    public List<ProfileInfo> getProfiles(OnvifDevice device) {
        long profilesStartTime = System.currentTimeMillis();
        log.info("=== 标准ONVIF协议配置文件获取诊断开始 ===");
        log.info("设备信息: {}:{}", device.getIpAddress(), device.getPort());
        log.info("设备制造商: {}", device.getManufacturer());
        log.info("设备型号: {}", device.getModel());
        log.info("ONVIF协议版本: 支持ONVIF Profile S/T标准");
        log.info("Media服务: 使用标准GetProfiles请求");
        log.info("开始时间戳: {}", profilesStartTime);
        
        try {
            log.info("开始获取设备配置文件: {}:{}", device.getIpAddress(), device.getPort());
            
            // 验证设备基本信息
            if (device.getIpAddress() == null || device.getPort() == null) {
                log.error("设备信息不完整: IP={}, Port={}", device.getIpAddress(), device.getPort());
                return new ArrayList<>();
            }
            
            // 验证认证信息
            if (device.getUsername() == null || device.getPassword() == null || 
                device.getUsername().trim().isEmpty() || device.getPassword().trim().isEmpty()) {
                log.error("设备认证信息缺失: username={}, password={}", 
                    device.getUsername(), device.getPassword() != null ? "[已设置]" : "null");
                return new ArrayList<>();
            }
            
            // 首先获取媒体服务地址（使用优化的重试机制）
            log.info(">>> 步骤1: 获取媒体服务地址");
            long mediaUrlStartTime = System.currentTimeMillis();
            String mediaServiceUrl = getMediaServiceUrlWithRetry(device);
            long mediaUrlTime = System.currentTimeMillis() - mediaUrlStartTime;
            
            if (StrUtil.isEmpty(mediaServiceUrl)) {
                log.error("无法获取ONVIF媒体服务地址: {}:{}, 耗时: {}ms", device.getIpAddress(), device.getPort(), mediaUrlTime);
                log.error("标准ONVIF协议媒体服务地址获取失败，可能原因:");
                log.error("1. 设备不支持ONVIF GetCapabilities请求（非标准ONVIF设备）");
                log.error("2. 网络连接问题导致ONVIF服务请求失败");
                log.error("3. 设备ONVIF Media服务未启用或异常");
                log.error("4. WS-Security认证失败或权限不足");
                log.error("5. 设备ONVIF协议版本不兼容（建议检查Profile S/T支持）");
                log.error("建议: 1.检查设备ONVIF服务状态 2.验证认证信息 3.确认网络连通性");
                return new ArrayList<>();
            }
            log.info("获取到媒体服务地址: {}, 耗时: {}ms", mediaServiceUrl, mediaUrlTime);
            
            // 针对海康设备，准备备用Media服务URL
            log.info(">>> 步骤2: 准备Media服务URL列表");
            List<String> mediaUrls;
            
            if (isHikvisionDevice(device)) {
                mediaUrls = generateHikvisionMediaUrls(device, mediaServiceUrl);
                log.info("海康设备准备尝试{}个Media服务URL", mediaUrls.size());
                for (int i = 0; i < mediaUrls.size(); i++) {
                    log.info("备选URL {}: {}", i + 1, mediaUrls.get(i));
                }
            } else {
                mediaUrls = new ArrayList<>();
                mediaUrls.add(mediaServiceUrl);
                log.info("非海康设备，使用单一Media服务URL: {}", mediaServiceUrl);
            }
            
            log.info(">>> 步骤3: 构建GetProfiles SOAP请求");
            String soapRequest = buildGetProfilesRequest();
            log.info("SOAP请求长度: {} 字符", soapRequest.length());
            log.debug("SOAP请求内容: {}", soapRequest);
            
            String response = null;
            String successUrl = null;
            
            log.info(">>> 步骤4: 发送GetProfiles请求");
            // 尝试所有可能的Media服务URL
            for (int urlIndex = 0; urlIndex < mediaUrls.size(); urlIndex++) {
                String url = mediaUrls.get(urlIndex);
                long requestStartTime = System.currentTimeMillis();
                try {
                    log.info("发送GetProfiles请求到URL {} (第{}/{}个): {}", urlIndex + 1, urlIndex + 1, mediaUrls.size(), url);
                    response = sendSoapRequestWithOptimization(url, soapRequest, device);
                    long requestTime = System.currentTimeMillis() - requestStartTime;
                    log.info("请求完成，耗时: {}ms，响应长度: {} 字符", requestTime, response != null ? response.length() : 0);
                    
                    if (StrUtil.isNotEmpty(response) && !response.contains("<soap:Fault>") && !response.contains("<env:Fault>")) {
                        successUrl = url;
                        log.info("✓ GetProfiles请求成功，使用URL: {}", url);
                        log.debug("成功响应内容前500字符: {}", response.length() > 500 ? response.substring(0, 500) : response);
                        break;
                    } else {
                        log.warn("✗ GetProfiles请求失败或返回错误，URL: {}", url);
                        if (StrUtil.isEmpty(response)) {
                            log.warn("响应为空，可能原因: 网络超时、设备无响应、连接中断");
                        } else {
                            log.warn("响应包含SOAP Fault，错误响应前300字符: {}", response.length() > 300 ? response.substring(0, 300) : response);
                        }
                    }
                } catch (Exception e) {
                    long requestTime = System.currentTimeMillis() - requestStartTime;
                    log.warn("✗ GetProfiles请求异常，URL: {}, 耗时: {}ms, 异常类型: {}, 异常信息: {}", url, requestTime, e.getClass().getSimpleName(), e.getMessage());
                    
                    // 详细分析异常类型
                    if (e.getMessage() != null) {
                        if (e.getMessage().contains("Unexpected end of file")) {
                            log.warn("检测到'Unexpected end of file'错误，分析:");
                            log.warn("- 网络连接在数据传输过程中被意外中断");
                            log.warn("- 设备在处理请求时超时或过载");
                            log.warn("- 防火墙或网络设备干扰了连接");
                            log.warn("- 建议检查网络稳定性和设备负载");
                        } else if (e.getMessage().contains("Connection reset")) {
                            log.warn("检测到'Connection reset'错误，可能是设备主动断开连接");
                        } else if (e.getMessage().contains("timeout")) {
                            log.warn("检测到超时错误，设备响应时间过长");
                        } else if (e.getMessage().contains("401")) {
                            log.warn("检测到认证失败(401)，用户名密码可能不正确");
                        }
                    }
                    
                    if (isHikvisionDevice(device) && e.getMessage() != null && e.getMessage().contains("401")) {
                        log.warn("海康设备Media服务认证失败，尝试下一个URL");
                    }
                    
                    analyzeConnectionError(e, device);
                }
            }
            
            log.info(">>> 步骤5: 分析GetProfiles响应");
            if (StrUtil.isEmpty(response)) {
                log.error("✗ 所有ONVIF Media服务URL都失败: {}:{}, 尝试了{}个URL", device.getIpAddress(), device.getPort(), mediaUrls.size());
                log.error("标准ONVIF GetProfiles请求失败，可能原因:");
                log.error("1. 设备ONVIF Media服务不可用或异常（检查ONVIF服务状态）");
                log.error("2. 网络连接持续不稳定（检查网络连通性）");
                log.error("3. 设备过载或ONVIF服务响应超时");
                log.error("4. WS-Security认证信息错误或权限不足");
                log.error("5. 防火墙阻止了ONVIF SOAP通信（端口通常为80/8080）");
                log.error("6. 设备不完全支持ONVIF Profile S标准");
                log.error("建议: 使用ONVIF Device Test Tool验证设备ONVIF兼容性");
                return new ArrayList<>();
            }
            log.info("✓ 获取到GetProfiles响应，长度: {} 字符", response.length());
            log.info("成功使用的URL: {}", successUrl);
            log.debug("响应内容前1000字符: {}", response.length() > 1000 ? response.substring(0, 1000) : response);
            
            // 检查响应是否包含错误
            log.info(">>> 步骤6: 检查SOAP错误");
            if (response.contains("<soap:Fault>") || response.contains("<env:Fault>")) {
                log.error("✗ ONVIF GetProfiles请求返回SOAP错误: {}:{}", device.getIpAddress(), device.getPort());
                log.error("SOAP错误响应前500字符: {}", response.substring(0, Math.min(500, response.length())));
                log.error("标准ONVIF协议SOAP错误可能原因:");
                log.error("1. ONVIF SOAP请求格式不符合标准");
                log.error("2. 设备不支持标准ONVIF GetProfiles操作");
                log.error("3. WS-Security认证失败或权限不足");
                log.error("4. 设备ONVIF服务内部错误");
                log.error("5. ONVIF协议版本不兼容（检查Profile S/T支持）");
                return new ArrayList<>();
            }
            log.info("✓ 响应无SOAP错误");
            
            // 记录完整的GetProfiles响应用于调试
            log.debug("GetProfiles完整响应内容: {}", response);
            
            // 检查响应是否包含配置文件 - 支持更多格式
            log.info(">>> 步骤7: 检查配置文件标签");
            boolean hasProfiles = response.contains("<trt:Profiles") || 
                                response.contains("<trt:Profile") ||
                                response.contains("<Profiles") ||
                                response.contains("<Profile") ||
                                response.toLowerCase().contains("profile");
            
            log.info("配置文件标签检查结果:");
            log.info("- <trt:Profiles>: {}", response.contains("<trt:Profiles"));
            log.info("- <trt:Profile>: {}", response.contains("<trt:Profile"));
            log.info("- <Profiles>: {}", response.contains("<Profiles"));
            log.info("- <Profile>: {}", response.contains("<Profile"));
            log.info("- 包含'profile'文本: {}", response.toLowerCase().contains("profile"));
            
            if (!hasProfiles) {
                log.warn("✗ ONVIF GetProfiles响应中未找到配置文件标签: {}:{}", device.getIpAddress(), device.getPort());
                log.warn("响应内容片段(前1000字符): {}", response.substring(0, Math.min(1000, response.length())));
                log.warn("标准ONVIF协议可能原因:");
                log.warn("1. 设备没有配置任何ONVIF媒体配置文件");
                log.warn("2. ONVIF响应格式不标准或使用了非标准命名空间");
                log.warn("3. 设备返回了空的ONVIF配置文件列表");
                log.warn("4. ONVIF XML解析可能需要特殊处理");
                log.warn("5. 设备ONVIF Profile S标准支持不完整");
                
                // 尝试分析响应内容
                analyzeProfilesResponse(response, device);
                return new ArrayList<>();
            }
            log.info("✓ 响应包含配置文件标签");
            
            log.info(">>> 步骤8: 解析配置文件");
            long parseStartTime = System.currentTimeMillis();
            List<ProfileInfo> profiles = parseProfilesWithEnhancement(response);
            long parseTime = System.currentTimeMillis() - parseStartTime;
            log.info("配置文件解析完成，耗时: {}ms，解析到 {} 个配置文件", parseTime, profiles.size());
            
            if (profiles.isEmpty()) {
                log.warn("✗ 虽然ONVIF响应包含配置文件标签，但解析结果为空: {}:{}", device.getIpAddress(), device.getPort());
                log.warn("标准ONVIF协议解析失败可能原因:");
                log.warn("1. ONVIF XML格式不标准或结构异常");
                log.warn("2. ONVIF命名空间不匹配或缺失");
                log.warn("3. 设备返回空的ONVIF配置文件列表");
                log.warn("4. 解析器无法识别的ONVIF XML结构");
                log.warn("5. 设备ONVIF Profile S/T标准实现不完整");
                
                // 尝试备用解析方法
                log.info("尝试使用备用解析方法: {}:{}", device.getIpAddress(), device.getPort());
                long fallbackStartTime = System.currentTimeMillis();
                profiles = parseProfilesWithFallback(response, device);
                long fallbackTime = System.currentTimeMillis() - fallbackStartTime;
                log.info("备用解析完成，耗时: {}ms，解析到 {} 个配置文件", fallbackTime, profiles.size());
                
                if (profiles.isEmpty()) {
                    log.error("所有解析方法都失败，无法获取Profile信息: {}:{}", device.getIpAddress(), device.getPort());
                    return new ArrayList<>();
                }
                
                log.info("备用解析方法成功解析到 {} 个Profile", profiles.size());
            }
            
            // 为每个配置文件获取流地址（使用优化的方法）
            log.info(">>> 步骤9: 获取RTSP流地址");
            int successCount = 0;
            long streamUriStartTime = System.currentTimeMillis();
            
            for (int i = 0; i < profiles.size(); i++) {
                ProfileInfo profile = profiles.get(i);
                log.info("为配置文件 {} (第{}/{}个) 获取流地址", profile.getToken(), i + 1, profiles.size());
                long singleStreamStartTime = System.currentTimeMillis();
                try {
                    String rtspUrl = getStreamUriWithOptimization(device, mediaServiceUrl, profile.getToken());
                    profile.setRtspUrl(rtspUrl);
                    long singleStreamTime = System.currentTimeMillis() - singleStreamStartTime;
                    
                    if (StrUtil.isNotEmpty(rtspUrl)) {
                        // 隐藏RTSP URL中的密码信息
                        String maskedRtspUrl = rtspUrl.replaceAll("://[^@]*@", "://***:***@");
                        log.info("✓ 配置文件 {} 的RTSP地址: {}，耗时: {}ms", profile.getToken(), maskedRtspUrl, singleStreamTime);
                        successCount++;
                    } else {
                        log.warn("✗ 配置文件 {} 未能获取到RTSP地址，耗时: {}ms", profile.getToken(), singleStreamTime);
                    }
                } catch (Exception e) {
                    long singleStreamTime = System.currentTimeMillis() - singleStreamStartTime;
                    log.warn("✗ 获取配置文件 {} 的RTSP地址失败，耗时: {}ms，错误: {}", profile.getToken(), singleStreamTime, e.getMessage());
                }
            }
            
            long streamUriTime = System.currentTimeMillis() - streamUriStartTime;
            long totalTime = System.currentTimeMillis() - profilesStartTime;
            
            log.info("=== 标准ONVIF协议配置文件获取诊断完成 ===");
            log.info("设备: {}:{}", device.getIpAddress(), device.getPort());
            log.info("总耗时: {}ms", totalTime);
            log.info("ONVIF Profile总数: {}", profiles.size());
            log.info("RTSP流地址获取成功数: {}", successCount);
            log.info("ONVIF兼容性成功率: {}%", profiles.size() > 0 ? (successCount * 100 / profiles.size()) : 0);
            log.info("ONVIF协议状态: 标准Profile S/T兼容设备");
            log.info("RTSP获取总耗时: {}ms", streamUriTime);
            log.info("平均每个配置文件耗时: {}ms", profiles.size() > 0 ? (streamUriTime / profiles.size()) : 0);
            
            if (successCount < profiles.size()) {
                log.warn("部分配置文件未能获取到RTSP地址，可能影响通道创建");
            }
            
            return profiles;
            
        } catch (Exception e) {
            long totalTime = System.currentTimeMillis() - profilesStartTime;
            log.error("=== 设备配置文件获取诊断失败 ===");
            log.error("设备: {}:{}", device.getIpAddress(), device.getPort());
            log.error("总耗时: {}ms", totalTime);
            log.error("异常类型: {}", e.getClass().getSimpleName());
            log.error("异常信息: {}", e.getMessage());
            log.error("失败原因分析:");
            
            if (e.getMessage() != null) {
                if (e.getMessage().contains("Unexpected end of file")) {
                    log.error("- 网络连接异常中断，建议检查网络稳定性");
                } else if (e.getMessage().contains("Connection reset")) {
                    log.error("- 设备主动断开连接，可能是设备过载或配置问题");
                } else if (e.getMessage().contains("timeout")) {
                    log.error("- 请求超时，建议增加超时时间或检查设备响应性能");
                } else if (e.getMessage().contains("401") || e.getMessage().contains("Unauthorized")) {
                    log.error("- 认证失败，请检查用户名密码是否正确");
                } else {
                    log.error("- 其他网络或设备相关问题");
                }
            }
            
            // 提供详细的错误分析
            analyzeConnectionError(e, device);
            
            return new ArrayList<>();
        }
    }

    /**
     * 获取流地址
     * 
     * @param device 设备
     * @param mediaServiceUrl 媒体服务地址
     * @param profileToken 配置文件Token
     * @return RTSP流地址
     */
    private String getStreamUri(OnvifDevice device, String mediaServiceUrl, String profileToken) {
        try {
            log.debug("开始获取流地址，Profile Token: {}", profileToken);
            
            // 获取设备配置
            String deviceType = device.getManufacturer() != null ? device.getManufacturer() : "default";
            DeviceConfigManager.DeviceConfig config = deviceConfigManager.getDeviceConfig(deviceType);
            
            String soapRequest = buildGetStreamUriRequest(profileToken);
            String response;
            
            // 对海康设备使用专用的SOAP请求方法
            if (isHikvisionDevice(device)) {
                response = sendSoapRequestForHikvision(mediaServiceUrl, soapRequest, device.getUsername(), device.getPassword(), config);
            } else {
                response = sendSoapRequest(mediaServiceUrl, soapRequest, device.getUsername(), device.getPassword());
            }
            
            if (StrUtil.isNotEmpty(response)) {
                log.debug("GetStreamUri响应长度: {} 字符", response.length());
                
                // 检查响应是否包含错误
                if (response.contains("<soap:Fault>") || response.contains("<env:Fault>")) {
                    log.error("GetStreamUri请求返回SOAP错误，Profile: {}, 响应: {}", 
                        profileToken, response.substring(0, Math.min(500, response.length())));
                    return "";
                }
                
                // 尝试多种URI提取模式
                String streamUri = extractStreamUri(response);
                
                if (StrUtil.isNotEmpty(streamUri)) {
                    log.debug("成功提取流地址: {}", streamUri);
                    return streamUri;
                } else {
                    log.warn("无法从响应中提取流地址，Profile: {}, 响应片段: {}", 
                        profileToken, response.substring(0, Math.min(200, response.length())));
                }
            } else {
                log.warn("GetStreamUri响应为空，Profile: {}", profileToken);
            }
            
        } catch (Exception e) {
            log.error("获取流地址失败，Profile: {} - {}", profileToken, e.getMessage(), e);
        }
        return "";
    }
    
    /**
     * 从GetStreamUri响应中提取流地址
     */
    private String extractStreamUri(String response) {
        // 支持多种URI标签格式
        String[] uriPatterns = {
            "<tt:Uri>", "</tt:Uri>",
            "<Uri>", "</Uri>",
            "<trt:Uri>", "</trt:Uri>",
            "<MediaUri>", "</MediaUri>",
            "<StreamUri>", "</StreamUri>"
        };
        
        for (int i = 0; i < uriPatterns.length; i += 2) {
            String uri = extractValue(response, uriPatterns[i], uriPatterns[i + 1]);
            if (StrUtil.isNotEmpty(uri)) {
                // 处理可能的XML转义字符
                uri = uri.replace("&amp;", "&")
                        .replace("&lt;", "<")
                        .replace("&gt;", ">")
                        .replace("&quot;", "\"")
                        .replace("&apos;", "'");
                
                // 验证URI格式
                if (uri.startsWith("rtsp://") || uri.startsWith("http://") || uri.startsWith("https://")) {
                    return uri;
                }
            }
        }
        
        return "";
    }
    


    /**
     * 获取媒体服务地址
     * 
     * @param device 设备
     * @return 媒体服务地址
     */
    private String getMediaServiceUrl(OnvifDevice device) {
        try {
            String serviceUrl = getDeviceServiceUrl(device);
            log.info("设备服务地址: {}", serviceUrl);
            
            String soapRequest = buildGetCapabilitiesRequest();
            log.info("发送GetCapabilities请求到: {}", serviceUrl);
            
            String response = sendSoapRequest(serviceUrl, soapRequest, device.getUsername(), device.getPassword());
            if (StrUtil.isNotEmpty(response)) {
                log.info("GetCapabilities响应长度: {} 字符", response.length());
                
                // 记录完整的GetCapabilities响应用于调试海康设备问题
                log.debug("GetCapabilities完整响应内容: {}", response);
                
                // 检查响应是否包含错误
                if (response.contains("<soap:Fault>") || response.contains("<env:Fault>")) {
                    log.error("GetCapabilities请求返回SOAP错误: {}:{}, 响应: {}", 
                        device.getIpAddress(), device.getPort(), response.substring(0, Math.min(500, response.length())));
                    return "";
                }
                
                // 尝试多种媒体服务地址提取模式，包括海康设备特有格式
                String mediaServiceUrl = null;
                String[] patterns = {
                    "<tt:Media>\\s*<tt:XAddr>(.*?)</tt:XAddr>",
                    "<tds:Media>\\s*<tds:XAddr>(.*?)</tds:XAddr>",
                    "<Media>\\s*<XAddr>(.*?)</XAddr>",
                    "<tt:XAddr[^>]*>(.*?)</tt:XAddr>.*Media",
                    "Media.*?<tt:XAddr[^>]*>(.*?)</tt:XAddr>",
                    // 海康设备特有格式
                    "<tds:Capabilities[^>]*>.*?<tds:Media[^>]*>.*?<tds:XAddr>(.*?)</tds:XAddr>",
                    "<Capabilities[^>]*>.*?<Media[^>]*>.*?<XAddr>(.*?)</XAddr>",
                    "Media.*?XAddr[^>]*>(.*?)</.*?XAddr"
                };
                
                for (String pattern : patterns) {
                    mediaServiceUrl = extractValue(response, pattern, "");
                    if (StrUtil.isNotEmpty(mediaServiceUrl)) {
                        log.info("使用模式 {} 提取到媒体服务地址: {}", pattern, mediaServiceUrl);
                        break;
                    }
                }
                
                // 如果常规模式都失败，尝试海康设备的特殊解析
                if (StrUtil.isEmpty(mediaServiceUrl)) {
                    String deviceType = detectDeviceTypeByDevice(device);
                    if ("hikvision".equals(deviceType)) {
                        mediaServiceUrl = parseHikvisionMediaServiceUrl(response, device);
                        if (StrUtil.isNotEmpty(mediaServiceUrl)) {
                            log.info("使用海康设备特殊解析提取到媒体服务地址: {}", mediaServiceUrl);
                        }
                    }
                }
                
                if (StrUtil.isNotEmpty(mediaServiceUrl)) {
                    log.info("从响应中提取到媒体服务地址: {}", mediaServiceUrl);
                    
                    // 检查媒体服务URL是否包含端口，如果没有则添加设备端口
                    if (!mediaServiceUrl.matches(".*://[^/]+:\\d+.*")) {
                        // URL没有端口，需要添加设备端口
                        String protocol = mediaServiceUrl.startsWith("https") ? "https" : "http";
                        String host = device.getIpAddress();
                        Integer port = device.getPort();
                        String path = mediaServiceUrl.substring(mediaServiceUrl.indexOf("/", 8)); // 跳过协议部分
                        
                        mediaServiceUrl = protocol + "://" + host + ":" + port + path;
                        log.info("媒体服务地址缺少端口，已补充为: {}", mediaServiceUrl);
                    }
                    
                    // 验证媒体服务地址是否可访问
                    try {
                        String testRequest = buildGetProfilesRequest();
                        String testResponse = sendSoapRequest(mediaServiceUrl, testRequest, device.getUsername(), device.getPassword());
                        if (StrUtil.isNotEmpty(testResponse) && !testResponse.contains("<soap:Fault>") && !testResponse.contains("<env:Fault>")) {
                            log.info("媒体服务地址验证成功: {}", mediaServiceUrl);
                            return mediaServiceUrl;
                        } else {
                            log.warn("媒体服务地址验证失败: {}, 响应: {}", mediaServiceUrl, 
                                testResponse != null ? testResponse.substring(0, Math.min(200, testResponse.length())) : "null");
                        }
                    } catch (Exception e) {
                        log.warn("媒体服务地址验证异常: {} - {}", mediaServiceUrl, e.getMessage());
                    }
                    
                    return mediaServiceUrl;
                } else {
                    log.error("无法从GetCapabilities响应中提取媒体服务地址，响应内容: {}", response.substring(0, Math.min(500, response.length())));
                    
                    // 尝试查找任何包含Media的XAddr
                    if (response.contains("Media") && response.contains("XAddr")) {
                        log.info("响应中包含Media和XAddr，但提取失败，可能是格式问题");
                    }
                    
                    // 对于海康设备，尝试使用常见的媒体服务路径
                    String deviceType = detectDeviceTypeByDevice(device);
                    if ("hikvision".equals(deviceType)) {
                        log.info("检测到海康设备，尝试使用默认媒体服务路径");
                        String[] hikvisionMediaPaths = {
                            "/onvif/media_service",
                            "/onvif/Media",
                            "/onvif/media",
                            "/onvif/Media2",
                            "/Media",
                            "/media"
                        };
                        
                        for (String path : hikvisionMediaPaths) {
                            String fallbackUrl = String.format("http://%s:%d%s", device.getIpAddress(), device.getPort(), path);
                            try {
                                log.info("尝试海康设备媒体服务路径: {}", fallbackUrl);
                                if (testMediaServiceUrl(fallbackUrl, device)) {
                                    log.info("海康设备找到可用的媒体服务路径: {}", fallbackUrl);
                                    return fallbackUrl;
                                }
                            } catch (Exception e) {
                                log.debug("海康设备媒体服务路径测试失败: {} - {}", fallbackUrl, e.getMessage());
                            }
                        }
                    }
                }
            } else {
                log.error("GetCapabilities请求返回空响应");
            }
            
        } catch (Exception e) {
            log.error("获取媒体服务地址失败: {}:{} - {}", device.getIpAddress(), device.getPort(), e.getMessage(), e);
            
            // 提供详细的错误分析
            if (e instanceof java.net.SocketTimeoutException) {
                log.error("GetCapabilities请求超时，建议检查网络连接和超时设置");
            } else if (e instanceof java.net.ConnectException) {
                log.error("无法连接到设备服务，请检查设备地址和ONVIF服务状态");
            } else if (e instanceof java.io.IOException && e.getMessage() != null) {
                if (e.getMessage().contains("401")) {
                    log.error("GetCapabilities认证失败，请检查设备认证信息");
                } else if (e.getMessage().contains("404")) {
                    log.error("设备服务路径不存在，请检查ONVIF服务配置");
                }
            }
        }
        return "";
    }

    /**
     * 获取设备服务地址
     * 
     * @param device 设备
     * @return 服务地址
     */
    private String getDeviceServiceUrl(OnvifDevice device) {
        if (StrUtil.isNotEmpty(device.getServiceUrl())) {
            return device.getServiceUrl();
        }
        
        // 检测设备类型，为海康设备尝试多个服务路径
        String deviceType = detectDeviceTypeByDevice(device);
        
        if ("hikvision".equals(deviceType)) {
            // 海康设备可能的服务路径
            String[] hikvisionPaths = {
                "/onvif/device_service",
                "/onvif/Device",
                "/onvif/device"
            };
            
            for (String path : hikvisionPaths) {
                String serviceUrl = String.format("http://%s:%d%s", device.getIpAddress(), device.getPort(), path);
                if (testServiceUrl(serviceUrl, device.getUsername(), device.getPassword())) {
                    log.info("海康设备找到可用的服务路径: {}", serviceUrl);
                    return serviceUrl;
                }
            }
        }
        
        return String.format("http://%s:%d/onvif/device_service", device.getIpAddress(), device.getPort());
    }

    /**
     * 发送SOAP请求
     * 
     * @param serviceUrl 服务地址
     * @param soapRequest SOAP请求
     * @param username 用户名
     * @param password 密码
     * @return 响应内容
     */
    private String sendSoapRequest(String serviceUrl, String soapRequest, String username, String password) throws IOException {
        long methodStartTime = System.currentTimeMillis();
        
        log.info("=== 开始SOAP请求处理 ===");
        log.info("目标URL: {}", serviceUrl);
        log.info("用户名: {}", username);
        log.info("请求长度: {} 字符", soapRequest.length());
        
        // 检测设备类型并获取对应配置
        String deviceType = detectDeviceType(serviceUrl);
        DeviceConfigManager.DeviceConfig config = deviceConfigManager.getDeviceConfig(deviceType);
        
        log.info("检测到设备类型: {}, 使用对应配置", deviceType != null ? deviceType : "默认");
        log.info("连接超时: {}ms, 读取超时: {}ms", config.getConnectionTimeout(), config.getReadTimeout());
        
        // 针对海康设备使用专门的连接处理逻辑
        if ("hikvision".equals(deviceType)) {
            log.info("使用海康设备专用处理逻辑");
            return sendSoapRequestForHikvision(serviceUrl, soapRequest, username, password, config);
        }
        
        try {
            return retryManager.executeWithRetry(() -> {
                log.info("开始重试机制执行SOAP请求");
                
                // 使用认证管理器按优先级尝试所有认证策略
                List<AuthenticationStrategy> strategies = authenticationManager.getStrategies();
                log.info("可用认证策略数量: {}", strategies.size());
                
                StringBuilder errorDetails = new StringBuilder();
                
                for (int i = 0; i < strategies.size(); i++) {
                    AuthenticationStrategy strategy = strategies.get(i);
                    long strategyStartTime = System.currentTimeMillis();
                    
                    try {
                        log.info("尝试认证策略 {}/{}: {} - {}", i + 1, strategies.size(), strategy.getAuthType(), serviceUrl);
                        
                        String response = sendSoapRequestWithAuth(serviceUrl, soapRequest, username, password, strategy, config);
                        
                        long strategyDuration = System.currentTimeMillis() - strategyStartTime;
                        
                        if (StrUtil.isNotEmpty(response)) {
                            log.info("{}认证成功: {}, 耗时: {}ms", strategy.getAuthType(), serviceUrl, strategyDuration);
                            log.info("响应长度: {} 字符", response.length());
                            return response;
                        } else {
                            log.warn("{}认证返回空响应: {}, 耗时: {}ms", strategy.getAuthType(), serviceUrl, strategyDuration);
                        }
                    } catch (Exception e) {
                        long strategyDuration = System.currentTimeMillis() - strategyStartTime;
                        String errorMsg = String.format("%s认证失败: %s - %s (耗时: %dms)", 
                            strategy.getAuthType(), serviceUrl, e.getMessage(), strategyDuration);
                        log.warn(errorMsg);
                        errorDetails.append(errorMsg).append("; ");
                        
                        // 记录更详细的错误信息
                        analyzeNetworkError(e, serviceUrl, strategy.getAuthType());
                    }
                }
                
                long totalMethodTime = System.currentTimeMillis() - methodStartTime;
                String finalError = String.format("所有认证策略都失败了: %s (总耗时: %dms). 详细错误: %s", 
                    serviceUrl, totalMethodTime, errorDetails.toString());
                log.error(finalError);
                throw new IOException(finalError);
            }, config, "SOAP请求: " + serviceUrl);
        } catch (Exception e) {
            long totalMethodTime = System.currentTimeMillis() - methodStartTime;
            log.error("=== SOAP请求处理失败 ===");
            log.error("URL: {}", serviceUrl);
            log.error("总耗时: {}ms", totalMethodTime);
            log.error("异常类型: {}", e.getClass().getSimpleName());
            log.error("异常信息: {}", e.getMessage());
            
            if (e instanceof IOException) {
                throw (IOException) e;
            }
            throw new IOException("SOAP请求失败: " + serviceUrl + " (耗时: " + totalMethodTime + "ms)", e);
        }
    }
    
    private String sendSoapRequestWithAuth(String serviceUrl, String soapRequest, String username, String password, AuthenticationStrategy strategy, DeviceConfigManager.DeviceConfig config) throws IOException {
        long authStartTime = System.currentTimeMillis();
        
        log.info("--- 开始{}认证请求 ---", strategy.getAuthType());
        log.info("URL: {}", serviceUrl);
        log.info("连接超时: {}ms, 读取超时: {}ms", config.getConnectionTimeout(), config.getReadTimeout());
        
        URL url = new URL(serviceUrl);
        log.info("解析URL - 主机: {}, 端口: {}, 路径: {}", url.getHost(), url.getPort(), url.getPath());
        
        HttpURLConnection connection = null;
        try {
            // 获取连接
            long connectionStartTime = System.currentTimeMillis();
            connection = connectionPoolManager.getConnection(url, config.getConnectionTimeout(), config.getReadTimeout());
            long connectionTime = System.currentTimeMillis() - connectionStartTime;
            log.info("获取连接耗时: {}ms", connectionTime);
            
            // 设置连接属性
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setDoInput(true);
            log.debug("设置连接属性完成");
            
            // 设置请求头
            connection.setRequestProperty("Content-Type", "application/soap+xml; charset=utf-8");
            connection.setRequestProperty("SOAPAction", "");
            connection.setRequestProperty("User-Agent", "ONVIF-Client/1.0");
            connection.setRequestProperty("Connection", "close"); // 强制关闭连接避免复用问题
            log.debug("设置基础请求头完成");
            
            // 处理WS-Security认证
            String finalSoapRequest = soapRequest;
            if (strategy instanceof WsSecurityAuthStrategy) {
                WsSecurityAuthStrategy wsSecurityStrategy = (WsSecurityAuthStrategy) strategy;
                String wsSecurityHeader = wsSecurityStrategy.buildWsSecurityHeader(username, password);
                if (StrUtil.isNotEmpty(wsSecurityHeader)) {
                    // 替换空的soap:Header为WS-Security头
                    finalSoapRequest = soapRequest.replace("<soap:Header/>", wsSecurityHeader)
                                                 .replace("<soap:Header></soap:Header>", wsSecurityHeader);
                    log.info("已嵌入WS-Security认证头: username={}", username);
                }
            } else {
                // 使用传统HTTP认证头
                String authHeader = strategy.buildAuthHeader(username, password, serviceUrl, "POST");
                if (StrUtil.isNotEmpty(authHeader)) {
                    connection.setRequestProperty("Authorization", authHeader);
                    log.info("已设置{}认证头: username={}", strategy.getAuthType(), username);
                } else {
                    log.info("使用{}认证策略，无需认证头", strategy.getAuthType());
                }
            }
            
            // 发送请求
            log.info("开始发送SOAP请求数据，长度: {} 字节", finalSoapRequest.getBytes(StandardCharsets.UTF_8).length);
            long sendStartTime = System.currentTimeMillis();
            
            try (var outputStream = connection.getOutputStream()) {
                outputStream.write(finalSoapRequest.getBytes(StandardCharsets.UTF_8));
                outputStream.flush();
            }
            
            long sendTime = System.currentTimeMillis() - sendStartTime;
            log.info("发送请求数据耗时: {}ms", sendTime);
            
            // 读取响应
            log.info("开始读取响应");
            long responseStartTime = System.currentTimeMillis();
            
            int responseCode = connection.getResponseCode();
            String responseMessage = connection.getResponseMessage();
            long responseTime = System.currentTimeMillis() - responseStartTime;
            
            log.info("收到HTTP响应 - 状态码: {}, 状态消息: {}, 耗时: {}ms ({}认证)", 
                responseCode, responseMessage, responseTime, strategy.getAuthType());
            
            if (responseCode == HttpURLConnection.HTTP_OK) {
                long readStartTime = System.currentTimeMillis();
                try (var inputStream = connection.getInputStream()) {
                    String response = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
                    long readTime = System.currentTimeMillis() - readStartTime;
                    long totalTime = System.currentTimeMillis() - authStartTime;
                    
                    log.info("{}认证成功 - 响应长度: {} 字符, 读取耗时: {}ms, 总耗时: {}ms", 
                        strategy.getAuthType(), response.length(), readTime, totalTime);
                    log.debug("响应内容前100字符: {}", response.substring(0, Math.min(response.length(), 100)));
                    return response;
                }
            } else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
                long totalTime = System.currentTimeMillis() - authStartTime;
                log.warn("{}认证失败 - 401未授权, 总耗时: {}ms", strategy.getAuthType(), totalTime);
                return ""; // 返回空字符串表示认证失败
            } else {
                // 读取错误响应
                String errorResponse = "";
                long errorReadStartTime = System.currentTimeMillis();
                try (var errorStream = connection.getErrorStream()) {
                    if (errorStream != null) {
                        errorResponse = new String(errorStream.readAllBytes(), StandardCharsets.UTF_8);
                    }
                } catch (Exception e) {
                    log.warn("读取错误响应失败: {}", e.getMessage());
                }
                long errorReadTime = System.currentTimeMillis() - errorReadStartTime;
                long totalTime = System.currentTimeMillis() - authStartTime;
                
                log.error("{}认证失败 - 状态码: {}, 状态消息: {}, 错误响应: {}, 错误读取耗时: {}ms, 总耗时: {}ms", 
                    strategy.getAuthType(), responseCode, responseMessage, 
                    errorResponse.length() > 200 ? errorResponse.substring(0, 200) + "..." : errorResponse,
                    errorReadTime, totalTime);
                return "";
            }
            
        } catch (Exception e) {
            long totalTime = System.currentTimeMillis() - authStartTime;
            log.error("{}认证异常 - 异常类型: {}, 异常信息: {}, 总耗时: {}ms", 
                strategy.getAuthType(), e.getClass().getSimpleName(), e.getMessage(), totalTime);
            throw e;
        } finally {
            // 归还连接到连接池
            if (connection != null) {
                try {
                    connectionPoolManager.returnConnection(connection);
                    log.debug("连接已归还到连接池");
                } catch (Exception e) {
                    log.warn("归还连接到连接池失败: {}", e.getMessage());
                }
            }
        }
    }
    


    /**
     * 分析HTTP错误
     */
    private String analyzeHttpError(int responseCode, String errorContent, String serviceUrl) {
        StringBuilder analysis = new StringBuilder();
        analysis.append(String.format("HTTP %d 错误分析: %s\n", responseCode, serviceUrl));
        
        switch (responseCode) {
            case 400:
                analysis.append("错误类型: 400 Bad Request - 请求格式错误\n");
                analysis.append("可能原因: 1) SOAP请求格式不正确 2) 请求参数无效 3) XML格式错误\n");
                analysis.append("建议: 1) 检查SOAP请求格式 2) 验证请求参数 3) 检查设备ONVIF版本兼容性\n");
                break;
            case 401:
                analysis.append("错误类型: 401 Unauthorized - 认证失败\n");
                analysis.append("可能原因: 1) 用户名密码错误 2) 认证方式不匹配 3) 用户权限不足\n");
                analysis.append("建议: 1) 验证用户名密码 2) 尝试不同认证方式 3) 检查用户权限设置\n");
                break;
            case 403:
                analysis.append("错误类型: 403 Forbidden - 访问被禁止\n");
                analysis.append("可能原因: 1) 用户权限不足 2) IP地址被限制 3) 设备访问控制\n");
                analysis.append("建议: 1) 检查用户权限 2) 验证IP白名单 3) 检查设备安全设置\n");
                break;
            case 404:
                analysis.append("错误类型: 404 Not Found - 服务路径不存在\n");
                analysis.append("可能原因: 1) ONVIF服务路径错误 2) 服务未启用 3) 设备不支持该服务\n");
                analysis.append("建议: 1) 尝试其他ONVIF路径 2) 检查设备ONVIF配置 3) 确认设备型号支持\n");
                break;
            case 500:
                analysis.append("错误类型: 500 Internal Server Error - 设备内部错误\n");
                analysis.append("可能原因: 1) 设备固件问题 2) 设备过载 3) 内部服务异常\n");
                analysis.append("建议: 1) 重启设备 2) 检查设备负载 3) 升级固件版本\n");
                break;
            case 503:
                analysis.append("错误类型: 503 Service Unavailable - 服务不可用\n");
                analysis.append("可能原因: 1) 设备过载 2) 服务临时停止 3) 维护模式\n");
                analysis.append("建议: 1) 稍后重试 2) 检查设备状态 3) 减少并发请求\n");
                break;
            default:
                analysis.append(String.format("错误类型: %d - 未知HTTP错误\n", responseCode));
                analysis.append("建议: 1) 检查网络连接 2) 查看设备日志 3) 联系设备厂商\n");
        }
        
        // 分析错误内容
        if (errorContent != null && !errorContent.isEmpty()) {
            if (errorContent.contains("<soap:Fault>") || errorContent.contains("<env:Fault>")) {
                analysis.append("响应包含SOAP错误信息\n");
            }
            if (errorContent.contains("<html>") || errorContent.contains("<HTML>")) {
                analysis.append("收到HTML响应，可能访问了Web管理界面\n");
            }
        }
        
        return analysis.toString();
    }
    
    /**
     * 分析网络错误
     */
    private void analyzeNetworkError(Exception e, String serviceUrl, String authType) {
        log.warn("=== 网络错误分析 ({}) ===", authType);
        log.warn("URL: {}", serviceUrl);
        log.warn("异常类型: {}", e.getClass().getSimpleName());
        log.warn("异常信息: {}", e.getMessage());
        
        if (e instanceof java.net.SocketTimeoutException) {
            log.warn("诊断: 连接超时");
            log.warn("建议: 1) 检查网络连接是否正常");
            log.warn("建议: 2) 增加连接超时时间");
            log.warn("建议: 3) 检查设备是否在线");
        } else if (e instanceof java.net.ConnectException) {
            log.warn("诊断: 连接被拒绝");
            log.warn("建议: 1) 检查设备IP地址和端口是否正确");
            log.warn("建议: 2) 检查设备ONVIF服务是否启用");
            log.warn("建议: 3) 检查防火墙设置");
        } else if (e instanceof java.net.UnknownHostException) {
            log.warn("诊断: 无法解析主机名");
            log.warn("建议: 1) 检查IP地址是否正确");
            log.warn("建议: 2) 检查DNS设置");
        } else if (e instanceof java.io.IOException) {
            String message = e.getMessage();
            if (message != null) {
                if (message.contains("401") || message.contains("Unauthorized")) {
                    log.warn("诊断: 认证失败");
                    log.warn("建议: 1) 检查用户名和密码是否正确");
                    log.warn("建议: 2) 检查设备认证方式设置");
                } else if (message.contains("404") || message.contains("Not Found")) {
                    log.warn("诊断: 服务路径不存在");
                    log.warn("建议: 1) 检查ONVIF服务地址是否正确");
                    log.warn("建议: 2) 尝试其他常见的ONVIF路径");
                } else if (message.contains("Connection reset")) {
                    log.warn("诊断: 连接被重置");
                    log.warn("建议: 1) 设备可能过载，稍后重试");
                    log.warn("建议: 2) 检查设备连接数限制");
                } else if (message.contains("Unexpected end of file")) {
                    log.warn("诊断: 连接异常中断");
                    log.warn("建议: 1) 网络不稳定，建议重试");
                    log.warn("建议: 2) 检查设备负载情况");
                } else if (message.contains("Read timed out")) {
                    log.warn("诊断: 读取超时");
                    log.warn("建议: 1) 增加读取超时时间");
                    log.warn("建议: 2) 检查设备响应性能");
                }
            }
        }
    }
    
    /**
     * 构建GetCapabilities请求
     * 
     * @return SOAP请求
     */
    private String buildGetCapabilitiesRequest() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
            "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" " +
            "xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\">" +
            "<soap:Header/>" +
            "<soap:Body>" +
            "<tds:GetCapabilities>" +
            "<tds:Category>All</tds:Category>" +
            "</tds:GetCapabilities>" +
            "</soap:Body>" +
            "</soap:Envelope>";
    }

    /**
     * 构建GetDeviceInformation请求
     * 
     * @return SOAP请求
     */
    private String buildGetDeviceInformationRequest() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
            "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" " +
            "xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\">" +
            "<soap:Header/>" +
            "<soap:Body>" +
            "<tds:GetDeviceInformation/>" +
            "</soap:Body>" +
            "</soap:Envelope>";
    }

    /**
     * 构建GetProfiles请求
     * 
     * @return SOAP请求
     */
    private String buildGetProfilesRequest() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
            "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" " +
            "xmlns:trt=\"http://www.onvif.org/ver10/media/wsdl\">" +
            "<soap:Header/>" +
            "<soap:Body>" +
            "<trt:GetProfiles/>" +
            "</soap:Body>" +
            "</soap:Envelope>";
    }

    /**
     * 构建GetStreamUri请求
     * 
     * @param profileToken 配置文件Token
     * @return SOAP请求
     */
    private String buildGetStreamUriRequest(String profileToken) {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
            "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" " +
            "xmlns:trt=\"http://www.onvif.org/ver10/media/wsdl\" " +
            "xmlns:tt=\"http://www.onvif.org/ver10/schema\">" +
            "<soap:Header/>" +
            "<soap:Body>" +
            "<trt:GetStreamUri>" +
            "<trt:StreamSetup>" +
            "<tt:Stream>RTP-Unicast</tt:Stream>" +
            "<tt:Transport><tt:Protocol>RTSP</tt:Protocol></tt:Transport>" +
            "</trt:StreamSetup>" +
            "<trt:ProfileToken>" + profileToken + "</trt:ProfileToken>" +
            "</trt:GetStreamUri>" +
            "</soap:Body>" +
            "</soap:Envelope>";
    }

    /**
     * 解析设备信息
     * 
     * @param response SOAP响应
     * @return 设备信息
     */
    private DeviceInfo parseDeviceInfo(String response) {
        DeviceInfo deviceInfo = new DeviceInfo();
        
        // 基本设备信息
        deviceInfo.setManufacturer(extractValue(response, "<tds:Manufacturer>", "</tds:Manufacturer>"));
        deviceInfo.setModel(extractValue(response, "<tds:Model>", "</tds:Model>"));
        deviceInfo.setFirmwareVersion(extractValue(response, "<tds:FirmwareVersion>", "</tds:FirmwareVersion>"));
        deviceInfo.setSerialNumber(extractValue(response, "<tds:SerialNumber>", "</tds:SerialNumber>"));
        deviceInfo.setHardwareId(extractValue(response, "<tds:HardwareId>", "</tds:HardwareId>"));
        
        // 扩展设备信息
        // 尝试从不同的标签中提取设备名称
        String deviceName = extractValue(response, "<tds:DeviceName>", "</tds:DeviceName>");
        if (StrUtil.isEmpty(deviceName)) {
            deviceName = extractValue(response, "<tds:Name>", "</tds:Name>");
        }
        deviceInfo.setDeviceName(deviceName);
        
        // 尝试提取设备类型
        String deviceType = extractValue(response, "<tds:DeviceType>", "</tds:DeviceType>");
        if (StrUtil.isEmpty(deviceType)) {
            // 根据制造商和型号推断设备类型
            String manufacturer = deviceInfo.getManufacturer();
            String model = deviceInfo.getModel();
            if (StrUtil.isNotEmpty(manufacturer) || StrUtil.isNotEmpty(model)) {
                String combined = (manufacturer + " " + model).toLowerCase();
                if (combined.contains("camera") || combined.contains("cam")) {
                    deviceType = "Camera";
                } else if (combined.contains("nvr") || combined.contains("recorder")) {
                    deviceType = "NVR";
                } else if (combined.contains("dvr")) {
                    deviceType = "DVR";
                } else {
                    deviceType = "Unknown";
                }
            }
        }
        deviceInfo.setDeviceType(deviceType);
        
        // 尝试提取位置信息
        String location = extractValue(response, "<tds:Location>", "</tds:Location>");
        if (StrUtil.isEmpty(location)) {
            location = extractValue(response, "<tds:PhysicalLocation>", "</tds:PhysicalLocation>");
        }
        deviceInfo.setLocation(location);
        
        // 尝试提取MAC地址（可能在网络接口信息中）
        String macAddress = extractValue(response, "<tds:HwAddress>", "</tds:HwAddress>");
        if (StrUtil.isEmpty(macAddress)) {
            macAddress = extractValue(response, "<tds:MACAddress>", "</tds:MACAddress>");
        }
        deviceInfo.setMacAddress(macAddress);
        
        return deviceInfo;
    }

    /**
     * 解析配置文件
     * 
     * @param response SOAP响应
     * @return 配置文件列表
     */
    private List<ProfileInfo> parseProfiles(String response) {
        List<ProfileInfo> profiles = new ArrayList<>();
        
        if (response == null || response.isEmpty()) {
            log.warn("SOAP响应为空，无法解析Profile信息");
            return profiles;
        }
        
        log.debug("开始解析Profile响应，响应长度: {}", response.length());
        
        // 支持多种Profile格式的正则表达式
        String[] profilePatterns = {
            "<trt:Profiles[^>]*token=\"([^\"]*)\">.*?</trt:Profiles>",
            "<[^:]*:Profiles[^>]*token=\"([^\"]*)\">.*?</[^:]*:Profiles>",
            "<Profiles[^>]*token=\"([^\"]*)\">.*?</Profiles>",
            "<trt:Profiles[^>]*Token=\"([^\"]*)\">.*?</trt:Profiles>",
            "<[^>]*Profile[^>]*token=\"([^\"]*)\">.*?</[^>]*Profile[^>]*>"
        };
        
        for (String patternStr : profilePatterns) {
            Pattern profilePattern = Pattern.compile(patternStr, Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
            Matcher profileMatcher = profilePattern.matcher(response);
            
            while (profileMatcher.find()) {
                String profileXml = profileMatcher.group(0);
                String token = profileMatcher.group(1);
                
                // 检查是否已经解析过这个token
                boolean alreadyExists = profiles.stream()
                    .anyMatch(p -> token.equals(p.getToken()));
                if (alreadyExists) {
                    continue;
                }
                
                log.debug("解析Profile，Token: {}", token);
                
                ProfileInfo profile = new ProfileInfo();
                profile.setToken(token);
                
                // 尝试多种Name标签格式
                String name = extractValue(profileXml, "<tt:Name>", "</tt:Name>");
                if (name.isEmpty()) {
                    name = extractValue(profileXml, "<Name>", "</Name>");
                }
                if (name.isEmpty()) {
                    name = "Profile_" + token; // 默认名称
                }
                profile.setName(name);
                
                // 解析视频配置 - 支持多种标签格式
                String videoEncoding = extractVideoEncoding(profileXml);
                profile.setVideoEncoding(videoEncoding);
                
                // 解析视频分辨率
                Integer width = extractIntegerValue(profileXml, 
                    new String[]{"<tt:Width>", "<Width>"}, 
                    new String[]{"</tt:Width>", "</Width>"});
                profile.setVideoWidth(width);
                
                Integer height = extractIntegerValue(profileXml, 
                    new String[]{"<tt:Height>", "<Height>"}, 
                    new String[]{"</tt:Height>", "</Height>"});
                profile.setVideoHeight(height);
                
                // 解析帧率
                Integer frameRate = extractIntegerValue(profileXml, 
                    new String[]{"<tt:FrameRateLimit>", "<FrameRateLimit>", "<tt:FrameRate>", "<FrameRate>"}, 
                    new String[]{"</tt:FrameRateLimit>", "</FrameRateLimit>", "</tt:FrameRate>", "</FrameRate>"});
                profile.setFrameRate(frameRate);
                
                // 解析音频配置
                String audioEncoding = extractAudioEncoding(profileXml);
                if (!audioEncoding.equals(videoEncoding)) {
                    profile.setAudioEncoding(audioEncoding);
                }
                
                profiles.add(profile);
                log.debug("成功解析Profile: {} ({}x{}, {}fps, 视频:{}, 音频:{})", 
                    name, width, height, frameRate, videoEncoding, audioEncoding);
            }
            
            // 如果找到了Profile，就不再尝试其他模式
            if (!profiles.isEmpty()) {
                break;
            }
        }
        
        log.info("共解析到 {} 个Profile", profiles.size());
        return profiles;
    }
    
    /**
     * 提取视频编码信息
     */
    private String extractVideoEncoding(String profileXml) {
        // 尝试从VideoEncoderConfiguration中提取
        String videoSection = extractValue(profileXml, "<tt:VideoEncoderConfiguration", "</tt:VideoEncoderConfiguration>");
        if (videoSection.isEmpty()) {
            videoSection = extractValue(profileXml, "<VideoEncoderConfiguration", "</VideoEncoderConfiguration>");
        }
        
        if (!videoSection.isEmpty()) {
            String encoding = extractValue(videoSection, "<tt:Encoding>", "</tt:Encoding>");
            if (encoding.isEmpty()) {
                encoding = extractValue(videoSection, "<Encoding>", "</Encoding>");
            }
            return encoding;
        }
        
        // 如果没有找到专门的视频配置，尝试通用的Encoding标签
        return extractValue(profileXml, "<tt:Encoding>", "</tt:Encoding>");
    }
    
    /**
     * 提取音频编码信息
     */
    private String extractAudioEncoding(String profileXml) {
        // 尝试从AudioEncoderConfiguration中提取
        String audioSection = extractValue(profileXml, "<tt:AudioEncoderConfiguration", "</tt:AudioEncoderConfiguration>");
        if (audioSection.isEmpty()) {
            audioSection = extractValue(profileXml, "<AudioEncoderConfiguration", "</AudioEncoderConfiguration>");
        }
        
        if (!audioSection.isEmpty()) {
            String encoding = extractValue(audioSection, "<tt:Encoding>", "</tt:Encoding>");
            if (encoding.isEmpty()) {
                encoding = extractValue(audioSection, "<Encoding>", "</Encoding>");
            }
            return encoding;
        }
        
        return "";
    }
    
    /**
     * 提取整数值，支持多种标签格式
     */
    private Integer extractIntegerValue(String xml, String[] startTags, String[] endTags) {
        for (int i = 0; i < startTags.length && i < endTags.length; i++) {
            String valueStr = extractValue(xml, startTags[i], endTags[i]);
            if (StrUtil.isNotEmpty(valueStr)) {
                try {
                    return Integer.parseInt(valueStr.trim());
                } catch (NumberFormatException e) {
                    log.debug("解析整数值失败: {}", valueStr);
                }
            }
        }
        return null;
    }

    /**
     * 从XML中提取指定标签的值
     * 增强版本，支持多种命名空间和XML格式
     * 
     * @param xml XML字符串
     * @param startTag 开始标签
     * @param endTag 结束标签
     * @return 提取的值
     */
    private String extractValue(String xml, String startTag, String endTag) {
        if (xml == null || xml.isEmpty()) {
            return "";
        }
        
        try {
            // 首先尝试原始的精确匹配
            Pattern pattern = Pattern.compile(Pattern.quote(startTag) + "(.*?)" + Pattern.quote(endTag), Pattern.DOTALL);
            Matcher matcher = pattern.matcher(xml);
            if (matcher.find()) {
                String value = matcher.group(1).trim();
                // 处理XML转义字符
                value = value.replace("&lt;", "<")
                           .replace("&gt;", ">")
                           .replace("&amp;", "&")
                           .replace("&quot;", "\"")
                           .replace("&apos;", "'");
                return value;
            }
            
            // 如果精确匹配失败，尝试更宽松的匹配（忽略命名空间前缀）
            String tagName = startTag.replaceAll("<[^:]*:", "<").replaceAll(">", ">")
                                   .replaceAll("</[^:]*:", "</");
            String endTagName = endTag.replaceAll("</[^:]*:", "</");
            
            // 构建更宽松的正则表达式
            String flexibleStartTag = tagName.replaceAll("<([^>]+)>", "<[^:]*:?$1[^>]*>");
            String flexibleEndTag = endTagName.replaceAll("</([^>]+)>", "</[^:]*:?$1>");
            
            Pattern flexiblePattern = Pattern.compile(flexibleStartTag + "(.*?)" + flexibleEndTag, 
                                                     Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
            Matcher flexibleMatcher = flexiblePattern.matcher(xml);
            if (flexibleMatcher.find()) {
                String value = flexibleMatcher.group(1).trim();
                // 处理XML转义字符
                value = value.replace("&lt;", "<")
                           .replace("&gt;", ">")
                           .replace("&amp;", "&")
                           .replace("&quot;", "\"")
                           .replace("&apos;", "'");
                return value;
            }
            
        } catch (Exception e) {
            log.warn("提取XML标签值时发生错误，标签: {}, 错误: {}", startTag, e.getMessage());
        }
        
        return "";
    }

    /**
     * 设备信息
     */
    public static class DeviceInfo {
        private String manufacturer;
        private String model;
        private String firmwareVersion;
        private String serialNumber;
        private String hardwareId;
        private String deviceType;
        private String location;
        private String macAddress;
        private String deviceName;

        // Getters and Setters
        public String getManufacturer() { return manufacturer; }
        public void setManufacturer(String manufacturer) { this.manufacturer = manufacturer; }
        
        public String getModel() { return model; }
        public void setModel(String model) { this.model = model; }
        
        public String getFirmwareVersion() { return firmwareVersion; }
        public void setFirmwareVersion(String firmwareVersion) { this.firmwareVersion = firmwareVersion; }
        
        public String getSerialNumber() { return serialNumber; }
        public void setSerialNumber(String serialNumber) { this.serialNumber = serialNumber; }
        
        public String getHardwareId() { return hardwareId; }
        public void setHardwareId(String hardwareId) { this.hardwareId = hardwareId; }
        
        public String getDeviceType() { return deviceType; }
        public void setDeviceType(String deviceType) { this.deviceType = deviceType; }
        
        public String getLocation() { return location; }
        public void setLocation(String location) { this.location = location; }
        
        public String getMacAddress() { return macAddress; }
        public void setMacAddress(String macAddress) { this.macAddress = macAddress; }
        
        public String getDeviceName() { return deviceName; }
        public void setDeviceName(String deviceName) { this.deviceName = deviceName; }
    }

    /**
     * 配置文件信息
     */
    public static class ProfileInfo {
        private String token;
        private String name;
        private String rtspUrl;
        private String videoEncoding;
        private Integer videoWidth;
        private Integer videoHeight;
        private Integer frameRate;
        private Integer bitRate;
        private String audioEncoding;

        // Getters and Setters
        public String getToken() { return token; }
        public void setToken(String token) { this.token = token; }
        
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        
        public String getRtspUrl() { return rtspUrl; }
        public void setRtspUrl(String rtspUrl) { this.rtspUrl = rtspUrl; }
        
        public String getVideoEncoding() { return videoEncoding; }
        public void setVideoEncoding(String videoEncoding) { this.videoEncoding = videoEncoding; }
        
        public Integer getVideoWidth() { return videoWidth; }
        public void setVideoWidth(Integer videoWidth) { this.videoWidth = videoWidth; }
        
        public Integer getVideoHeight() { return videoHeight; }
        public void setVideoHeight(Integer videoHeight) { this.videoHeight = videoHeight; }
        
        public Integer getFrameRate() { return frameRate; }
        public void setFrameRate(Integer frameRate) { this.frameRate = frameRate; }
        
        public Integer getBitRate() { return bitRate; }
        public void setBitRate(Integer bitRate) { this.bitRate = bitRate; }
        
        public String getAudioEncoding() { return audioEncoding; }
        public void setAudioEncoding(String audioEncoding) { this.audioEncoding = audioEncoding; }
    }

    // ========== 通道配置相关方法 ==========

    /**
     * 获取通道配置
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @return 通道配置
     */
    public Map<String, Object> getChannelConfiguration(OnvifDevice device, String profileToken) {
        Map<String, Object> config = new HashMap<>();
        
        try {
            String mediaServiceUrl = getMediaServiceUrl(device);
            if (StrUtil.isEmpty(mediaServiceUrl)) {
                throw new RuntimeException("无法获取媒体服务地址");
            }
            
            // 获取视频配置
            String videoConfigRequest = buildGetVideoEncoderConfigurationRequest(profileToken);
            String videoResponse = sendSoapRequest(mediaServiceUrl, videoConfigRequest, device.getUsername(), device.getPassword());
            
            if (StrUtil.isNotEmpty(videoResponse)) {
                config.put("videoConfig", parseVideoConfiguration(videoResponse));
            }
            
            // 获取音频配置
            String audioConfigRequest = buildGetAudioEncoderConfigurationRequest(profileToken);
            String audioResponse = sendSoapRequest(mediaServiceUrl, audioConfigRequest, device.getUsername(), device.getPassword());
            
            if (StrUtil.isNotEmpty(audioResponse)) {
                config.put("audioConfig", parseAudioConfiguration(audioResponse));
            }
            
        } catch (Exception e) {
            log.error("获取通道配置失败: {}", e.getMessage());
            throw new RuntimeException("获取通道配置失败: " + e.getMessage());
        }
        
        return config;
    }

    /**
     * 更新通道配置
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @param config 配置信息
     */
    public void updateChannelConfiguration(OnvifDevice device, String profileToken, Map<String, Object> config) {
        try {
            String mediaServiceUrl = getMediaServiceUrl(device);
            if (StrUtil.isEmpty(mediaServiceUrl)) {
                throw new RuntimeException("无法获取媒体服务地址");
            }
            
            // 更新视频配置
            if (config.containsKey("videoConfig")) {
                @SuppressWarnings("unchecked")
                Map<String, Object> videoConfig = (Map<String, Object>) config.get("videoConfig");
                String videoConfigRequest = buildSetVideoEncoderConfigurationRequest(profileToken, videoConfig);
                sendSoapRequest(mediaServiceUrl, videoConfigRequest, device.getUsername(), device.getPassword());
            }
            
            // 更新音频配置
            if (config.containsKey("audioConfig")) {
                @SuppressWarnings("unchecked")
                Map<String, Object> audioConfig = (Map<String, Object>) config.get("audioConfig");
                String audioConfigRequest = buildSetAudioEncoderConfigurationRequest(profileToken, audioConfig);
                sendSoapRequest(mediaServiceUrl, audioConfigRequest, device.getUsername(), device.getPassword());
            }
            
        } catch (Exception e) {
            log.error("更新通道配置失败: {}", e.getMessage());
            throw new RuntimeException("更新通道配置失败: " + e.getMessage());
        }
    }

    // ========== 预设位相关方法 ==========

    /**
     * 获取预设位列表
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @return 预设位列表
     */
    public List<Map<String, Object>> getPresets(OnvifDevice device, String profileToken) {
        try {
            String ptzServiceUrl = getPtzServiceUrl(device);
            if (StrUtil.isEmpty(ptzServiceUrl)) {
                throw new RuntimeException("设备不支持PTZ功能");
            }
            
            String soapRequest = buildGetPresetsRequest(profileToken);
            String response = sendSoapRequest(ptzServiceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            return parsePresets(response);
            
        } catch (Exception e) {
            log.error("获取预设位列表失败: {}", e.getMessage());
            throw new RuntimeException("获取预设位列表失败: " + e.getMessage());
        }
    }

    /**
     * 设置预设位
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @param preset 预设位信息
     * @return 设置结果
     */
    public Map<String, Object> setPreset(OnvifDevice device, String profileToken, Map<String, Object> preset) {
        try {
            String ptzServiceUrl = getPtzServiceUrl(device);
            if (StrUtil.isEmpty(ptzServiceUrl)) {
                throw new RuntimeException("设备不支持PTZ功能");
            }
            
            String presetName = (String) preset.get("name");
            String soapRequest = buildSetPresetRequest(profileToken, presetName);
            String response = sendSoapRequest(ptzServiceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("presetToken", extractPresetToken(response));
            result.put("message", "预设位设置成功");
            
            return result;
            
        } catch (Exception e) {
            log.error("设置预设位失败: {}", e.getMessage());
            throw new RuntimeException("设置预设位失败: " + e.getMessage());
        }
    }

    /**
     * 调用预设位
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @param presetToken 预设位令牌
     * @return 调用结果
     */
    public Map<String, Object> gotoPreset(OnvifDevice device, String profileToken, String presetToken) {
        try {
            String ptzServiceUrl = getPtzServiceUrl(device);
            if (StrUtil.isEmpty(ptzServiceUrl)) {
                throw new RuntimeException("设备不支持PTZ功能");
            }
            
            String soapRequest = buildGotoPresetRequest(profileToken, presetToken);
            sendSoapRequest(ptzServiceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("message", "预设位调用成功");
            
            return result;
            
        } catch (Exception e) {
            log.error("调用预设位失败: {}", e.getMessage());
            throw new RuntimeException("调用预设位失败: " + e.getMessage());
        }
    }

    /**
     * 删除预设位
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @param presetToken 预设位令牌
     */
    public void removePreset(OnvifDevice device, String profileToken, String presetToken) {
        try {
            String ptzServiceUrl = getPtzServiceUrl(device);
            if (StrUtil.isEmpty(ptzServiceUrl)) {
                throw new RuntimeException("设备不支持PTZ功能");
            }
            
            String soapRequest = buildRemovePresetRequest(profileToken, presetToken);
            sendSoapRequest(ptzServiceUrl, soapRequest, device.getUsername(), device.getPassword());
            
        } catch (Exception e) {
            log.error("删除预设位失败: {}", e.getMessage());
            throw new RuntimeException("删除预设位失败: " + e.getMessage());
        }
    }

    // ========== PTZ控制相关方法 ==========

    /**
     * PTZ控制
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @param command PTZ命令
     * @return 控制结果
     */
    public Map<String, Object> ptzControl(OnvifDevice device, String profileToken, Map<String, Object> command) {
        try {
            String ptzServiceUrl = getPtzServiceUrl(device);
            if (StrUtil.isEmpty(ptzServiceUrl)) {
                throw new RuntimeException("设备不支持PTZ功能");
            }
            
            String action = (String) command.get("action");
            String soapRequest;
            
            switch (action) {
                case "move":
                    soapRequest = buildContinuousMoveRequest(profileToken, command);
                    break;
                case "stop":
                    soapRequest = buildStopRequest(profileToken);
                    break;
                case "relative":
                    soapRequest = buildRelativeMoveRequest(profileToken, command);
                    break;
                case "absolute":
                    soapRequest = buildAbsoluteMoveRequest(profileToken, command);
                    break;
                default:
                    throw new RuntimeException("不支持的PTZ操作: " + action);
            }
            
            sendSoapRequest(ptzServiceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("message", "PTZ控制成功");
            
            return result;
            
        } catch (Exception e) {
            log.error("PTZ控制失败: {}", e.getMessage());
            throw new RuntimeException("PTZ控制失败: " + e.getMessage());
        }
    }

    // ========== 录像相关方法 ==========

    /**
     * 获取录像列表
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param page 页码
     * @param size 页大小
     * @return 录像列表
     */
    public Map<String, Object> getRecordings(OnvifDevice device, String profileToken, String startTime, String endTime, int page, int size) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 模拟录像数据，实际应该从设备获取
            List<Map<String, Object>> recordings = new ArrayList<>();
            
            // 这里应该实现真实的ONVIF录像查询逻辑
            // 由于ONVIF录像查询比较复杂，这里提供一个基础框架
            
            result.put("recordings", recordings);
            result.put("total", recordings.size());
            result.put("page", page);
            result.put("size", size);
            
        } catch (Exception e) {
            log.error("获取录像列表失败: {}", e.getMessage());
            throw new RuntimeException("获取录像列表失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 开始录像
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @param config 录像配置
     * @return 录像结果
     */
    public Map<String, Object> startRecording(OnvifDevice device, String profileToken, Map<String, Object> config) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 这里应该实现真实的ONVIF录像开始逻辑
            result.put("success", true);
            result.put("recordingId", "recording_" + System.currentTimeMillis());
            result.put("message", "录像开始成功");
            
        } catch (Exception e) {
            log.error("开始录像失败: {}", e.getMessage());
            throw new RuntimeException("开始录像失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 停止录像
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @return 停止结果
     */
    public Map<String, Object> stopRecording(OnvifDevice device, String profileToken) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 这里应该实现真实的ONVIF录像停止逻辑
            result.put("success", true);
            result.put("message", "录像停止成功");
            
        } catch (Exception e) {
            log.error("停止录像失败: {}", e.getMessage());
            throw new RuntimeException("停止录像失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 下载录像
     * 
     * @param device 设备
     * @param recordingId 录像ID
     * @return 下载响应
     */
    public ResponseEntity<byte[]> downloadRecording(OnvifDevice device, String recordingId) {
        try {
            // 这里应该实现真实的录像下载逻辑
            // 目前返回一个空的响应
            byte[] content = new byte[0];
            
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData("attachment", recordingId + ".mp4");
            
            return ResponseEntity.ok()
                .headers(headers)
                .body(content);
                
        } catch (Exception e) {
            log.error("下载录像失败: {}", e.getMessage());
            throw new RuntimeException("下载录像失败: " + e.getMessage());
        }
    }

    /**
     * 删除录像
     * 
     * @param device 设备
     * @param recordingId 录像ID
     */
    public void deleteRecording(OnvifDevice device, String recordingId) {
        try {
            // 这里应该实现真实的录像删除逻辑
            log.info("删除录像: {}", recordingId);
            
        } catch (Exception e) {
            log.error("删除录像失败: {}", e.getMessage());
            throw new RuntimeException("删除录像失败: " + e.getMessage());
        }
    }

    // ========== 事件相关方法 ==========

    /**
     * 获取事件列表
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @param page 页码
     * @param size 页大小
     * @return 事件列表
     */
    public Map<String, Object> getEvents(OnvifDevice device, String profileToken, int page, int size) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 模拟事件数据
            List<Map<String, Object>> events = new ArrayList<>();
            
            result.put("events", events);
            result.put("total", events.size());
            result.put("page", page);
            result.put("size", size);
            
        } catch (Exception e) {
            log.error("获取事件列表失败: {}", e.getMessage());
            throw new RuntimeException("获取事件列表失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 订阅事件
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @param subscription 订阅配置
     * @return 订阅结果
     */
    public Map<String, Object> subscribeEvents(OnvifDevice device, String profileToken, Map<String, Object> subscription) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 这里应该实现真实的事件订阅逻辑
            result.put("success", true);
            result.put("subscriptionId", "sub_" + System.currentTimeMillis());
            result.put("message", "事件订阅成功");
            
        } catch (Exception e) {
            log.error("订阅事件失败: {}", e.getMessage());
            throw new RuntimeException("订阅事件失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 取消订阅事件
     * 
     * @param device 设备
     * @param profileToken 配置文件令牌
     * @return 取消订阅结果
     */
    public Map<String, Object> unsubscribeEvents(OnvifDevice device, String profileToken) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 这里应该实现真实的取消订阅逻辑
            result.put("success", true);
            result.put("message", "取消订阅成功");
            
        } catch (Exception e) {
            log.error("取消订阅失败: {}", e.getMessage());
            throw new RuntimeException("取消订阅失败: " + e.getMessage());
        }
        
        return result;
    }

    // ========== 设备扩展功能方法 ==========

    /**
     * 同步设备时间
     * 
     * @param device ONVIF设备
     * @return 同步结果
     */
    public Map<String, Object> syncDeviceTime(OnvifDevice device) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            String serviceUrl = getDeviceServiceUrl(device);
            String soapRequest = buildSyncTimeRequest();
            String response = sendSoapRequest(serviceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            result.put("success", true);
            result.put("message", "设备时间同步成功");
            result.put("syncTime", new Date());
            
        } catch (Exception e) {
            log.error("同步设备时间失败: {}", e.getMessage());
            result.put("success", false);
            result.put("message", "同步设备时间失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 获取设备网络配置
     * 
     * @param device ONVIF设备
     * @return 网络配置
     */
    public Map<String, Object> getDeviceNetworkConfig(OnvifDevice device) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            String serviceUrl = getDeviceServiceUrl(device);
            String soapRequest = buildGetNetworkConfigRequest();
            String response = sendSoapRequest(serviceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            // 解析网络配置响应
            Map<String, Object> networkConfig = parseNetworkConfigResponse(response);
            
            result.put("success", true);
            result.put("networkConfig", networkConfig);
            
        } catch (Exception e) {
            log.error("获取设备网络配置失败: {}", e.getMessage());
            result.put("success", false);
            result.put("message", "获取设备网络配置失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 更新设备网络配置
     * 
     * @param device ONVIF设备
     * @param config 网络配置
     * @return 更新结果
     */
    public Map<String, Object> updateDeviceNetworkConfig(OnvifDevice device, Map<String, Object> config) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            String serviceUrl = getDeviceServiceUrl(device);
            String soapRequest = buildUpdateNetworkConfigRequest(config);
            String response = sendSoapRequest(serviceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            result.put("success", true);
            result.put("message", "网络配置更新成功");
            
        } catch (Exception e) {
            log.error("更新设备网络配置失败: {}", e.getMessage());
            result.put("success", false);
            result.put("message", "更新设备网络配置失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 重启设备
     * 
     * @param device ONVIF设备
     * @return 重启结果
     */
    public Map<String, Object> rebootDevice(OnvifDevice device) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            String serviceUrl = getDeviceServiceUrl(device);
            String soapRequest = buildRebootDeviceRequest();
            String response = sendSoapRequest(serviceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            result.put("success", true);
            result.put("message", "设备重启命令发送成功");
            
        } catch (Exception e) {
            log.error("重启设备失败: {}", e.getMessage());
            result.put("success", false);
            result.put("message", "重启设备失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 获取设备能力集
     * 
     * @param device ONVIF设备
     * @return 设备能力集
     */
    public Map<String, Object> getDeviceCapabilities(OnvifDevice device) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            String serviceUrl = getDeviceServiceUrl(device);
            String soapRequest = buildGetCapabilitiesRequest();
            String response = sendSoapRequest(serviceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            // 解析能力集响应
            Map<String, Object> capabilities = parseCapabilitiesResponse(response);
            
            result.put("success", true);
            result.put("capabilities", capabilities);
            
        } catch (Exception e) {
            log.error("获取设备能力集失败: {}", e.getMessage());
            result.put("success", false);
            result.put("message", "获取设备能力集失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 获取设备性能指标
     * 
     * @param device ONVIF设备
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param interval 时间间隔
     * @return 性能指标
     */
    public Map<String, Object> getDeviceMetrics(OnvifDevice device, String startTime, String endTime, String interval) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 模拟性能指标数据，实际应该从设备获取
            Map<String, Object> metrics = new HashMap<>();
            metrics.put("cpuUsage", Math.random() * 100);
            metrics.put("memoryUsage", Math.random() * 100);
            metrics.put("networkTraffic", Math.random() * 1000);
            metrics.put("temperature", 25 + Math.random() * 50);
            metrics.put("uptime", System.currentTimeMillis() / 1000);
            
            result.put("success", true);
            result.put("metrics", metrics);
            result.put("startTime", startTime);
            result.put("endTime", endTime);
            result.put("interval", interval);
            
        } catch (Exception e) {
            log.error("获取设备性能指标失败: {}", e.getMessage());
            result.put("success", false);
            result.put("message", "获取设备性能指标失败: " + e.getMessage());
        }
        
        return result;
    }

    // ========== 辅助方法 ==========

    /**
     * 获取PTZ服务地址
     */
    private String getPtzServiceUrl(OnvifDevice device) {
        // 这里应该从设备能力中获取PTZ服务地址
        // 目前返回一个默认地址
        return String.format("http://%s:%d/onvif/ptz_service", device.getIpAddress(), device.getPort());
    }

    /**
     * 解析视频配置
     */
    private Map<String, Object> parseVideoConfiguration(String response) {
        Map<String, Object> config = new HashMap<>();
        // 这里应该解析SOAP响应中的视频配置信息
        return config;
    }

    /**
     * 解析音频配置
     */
    private Map<String, Object> parseAudioConfiguration(String response) {
        Map<String, Object> config = new HashMap<>();
        // 这里应该解析SOAP响应中的音频配置信息
        return config;
    }

    /**
     * 解析预设位列表
     */
    private List<Map<String, Object>> parsePresets(String response) {
        List<Map<String, Object>> presets = new ArrayList<>();
        // 这里应该解析SOAP响应中的预设位信息
        return presets;
    }

    /**
     * 提取预设位令牌
     */
    private String extractPresetToken(String response) {
        // 这里应该从SOAP响应中提取预设位令牌
        return "preset_" + System.currentTimeMillis();
    }

    /**
     * 构建同步时间SOAP请求
     */
    private String buildSyncTimeRequest() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
               "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" " +
               "xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tds:SetSystemDateAndTime>" +
               "<tds:DateTimeType>NTP</tds:DateTimeType>" +
               "<tds:DaylightSavings>false</tds:DaylightSavings>" +
               "<tds:TimeZone><tds:TZ>CST-8</tds:TZ></tds:TimeZone>" +
               "</tds:SetSystemDateAndTime>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    /**
     * 构建获取网络配置SOAP请求
     */
    private String buildGetNetworkConfigRequest() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
               "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" " +
               "xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tds:GetNetworkInterfaces/>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    /**
     * 构建更新网络配置SOAP请求
     */
    private String buildUpdateNetworkConfigRequest(Map<String, Object> config) {
        String interfaceToken = (String) config.getOrDefault("interfaceToken", "eth0");
        String ipAddress = (String) config.getOrDefault("ipAddress", "192.168.1.100");
        String subnetMask = (String) config.getOrDefault("subnetMask", "255.255.255.0");
        String gateway = (String) config.getOrDefault("gateway", "192.168.1.1");
        
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
               "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" " +
               "xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tds:SetNetworkInterfaces>" +
               "<tds:InterfaceToken>" + interfaceToken + "</tds:InterfaceToken>" +
               "<tds:NetworkInterface>" +
               "<tds:IPv4><tds:Config>" +
               "<tds:Manual>" +
               "<tds:Address>" + ipAddress + "</tds:Address>" +
               "<tds:PrefixLength>24</tds:PrefixLength>" +
               "</tds:Manual>" +
               "</tds:Config></tds:IPv4>" +
               "</tds:NetworkInterface>" +
               "</tds:SetNetworkInterfaces>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    /**
     * 构建重启设备SOAP请求
     */
    private String buildRebootDeviceRequest() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
               "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" " +
               "xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tds:SystemReboot/>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    /**
     * 解析网络配置响应
     */
    private Map<String, Object> parseNetworkConfigResponse(String response) {
        Map<String, Object> config = new HashMap<>();
        
        // 简化的解析逻辑，实际应该使用XML解析器
        config.put("interfaceToken", "eth0");
        config.put("name", "Ethernet Interface");
        config.put("enabled", true);
        config.put("ipAddress", "192.168.1.100");
        config.put("subnetMask", "255.255.255.0");
        config.put("gateway", "192.168.1.1");
        config.put("dhcp", false);
        
        return config;
    }

    /**
     * 解析能力集响应
     */
    private Map<String, Object> parseCapabilitiesResponse(String response) {
        Map<String, Object> capabilities = new HashMap<>();
        
        // 简化的解析逻辑，实际应该使用XML解析器
        capabilities.put("device", true);
        capabilities.put("media", true);
        capabilities.put("ptz", true);
        capabilities.put("imaging", true);
        capabilities.put("events", true);
        capabilities.put("analytics", false);
        capabilities.put("recording", true);
        
        return capabilities;
    }

    // ========== SOAP请求构建方法 ==========

    private String buildGetVideoEncoderConfigurationRequest(String profileToken) {
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:trt=\"http://www.onvif.org/ver10/media/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<trt:GetVideoEncoderConfiguration>" +
               "<trt:ConfigurationToken>" + profileToken + "</trt:ConfigurationToken>" +
               "</trt:GetVideoEncoderConfiguration>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildGetAudioEncoderConfigurationRequest(String profileToken) {
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:trt=\"http://www.onvif.org/ver10/media/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<trt:GetAudioEncoderConfiguration>" +
               "<trt:ConfigurationToken>" + profileToken + "</trt:ConfigurationToken>" +
               "</trt:GetAudioEncoderConfiguration>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildSetVideoEncoderConfigurationRequest(String profileToken, Map<String, Object> config) {
        // 构建设置视频编码配置的SOAP请求
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:trt=\"http://www.onvif.org/ver10/media/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<trt:SetVideoEncoderConfiguration>" +
               "<!-- 配置内容 -->" +
               "</trt:SetVideoEncoderConfiguration>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildSetAudioEncoderConfigurationRequest(String profileToken, Map<String, Object> config) {
        // 构建设置音频编码配置的SOAP请求
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:trt=\"http://www.onvif.org/ver10/media/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<trt:SetAudioEncoderConfiguration>" +
               "<!-- 配置内容 -->" +
               "</trt:SetAudioEncoderConfiguration>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildGetPresetsRequest(String profileToken) {
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tptz=\"http://www.onvif.org/ver20/ptz/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tptz:GetPresets>" +
               "<tptz:ProfileToken>" + profileToken + "</tptz:ProfileToken>" +
               "</tptz:GetPresets>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildSetPresetRequest(String profileToken, String presetName) {
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tptz=\"http://www.onvif.org/ver20/ptz/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tptz:SetPreset>" +
               "<tptz:ProfileToken>" + profileToken + "</tptz:ProfileToken>" +
               "<tptz:PresetName>" + presetName + "</tptz:PresetName>" +
               "</tptz:SetPreset>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildGotoPresetRequest(String profileToken, String presetToken) {
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tptz=\"http://www.onvif.org/ver20/ptz/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tptz:GotoPreset>" +
               "<tptz:ProfileToken>" + profileToken + "</tptz:ProfileToken>" +
               "<tptz:PresetToken>" + presetToken + "</tptz:PresetToken>" +
               "</tptz:GotoPreset>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildRemovePresetRequest(String profileToken, String presetToken) {
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tptz=\"http://www.onvif.org/ver20/ptz/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tptz:RemovePreset>" +
               "<tptz:ProfileToken>" + profileToken + "</tptz:ProfileToken>" +
               "<tptz:PresetToken>" + presetToken + "</tptz:PresetToken>" +
               "</tptz:RemovePreset>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildContinuousMoveRequest(String profileToken, Map<String, Object> command) {
        // 构建连续移动的SOAP请求
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tptz=\"http://www.onvif.org/ver20/ptz/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tptz:ContinuousMove>" +
               "<tptz:ProfileToken>" + profileToken + "</tptz:ProfileToken>" +
               "<!-- 移动参数 -->" +
               "</tptz:ContinuousMove>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildStopRequest(String profileToken) {
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tptz=\"http://www.onvif.org/ver20/ptz/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tptz:Stop>" +
               "<tptz:ProfileToken>" + profileToken + "</tptz:ProfileToken>" +
               "</tptz:Stop>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildRelativeMoveRequest(String profileToken, Map<String, Object> command) {
        // 构建相对移动的SOAP请求
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tptz=\"http://www.onvif.org/ver20/ptz/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tptz:RelativeMove>" +
               "<tptz:ProfileToken>" + profileToken + "</tptz:ProfileToken>" +
               "<!-- 移动参数 -->" +
               "</tptz:RelativeMove>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }

    private String buildAbsoluteMoveRequest(String profileToken, Map<String, Object> command) {
        // 构建绝对移动的SOAP请求
        return "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tptz=\"http://www.onvif.org/ver20/ptz/wsdl\">" +
               "<soap:Header/>" +
               "<soap:Body>" +
               "<tptz:AbsoluteMove>" +
               "<tptz:ProfileToken>" + profileToken + "</tptz:ProfileToken>" +
               "<!-- 移动参数 -->" +
               "</tptz:AbsoluteMove>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }
    
    /**
     * 检测设备类型
     * 
     * @param serviceUrl 服务URL
     * @return 设备类型（hikvision、dahua、uniview、axis等）
     */
    private String detectDeviceType(String serviceUrl) {
        if (StrUtil.isEmpty(serviceUrl)) {
            return null;
        }
        
        String url = serviceUrl.toLowerCase();
        
        // 通过URL特征检测设备类型
        if (url.contains("hikvision") || url.contains("hik") || 
            url.contains("/onvif/device_service") || url.contains("/onvif/media_service")) {
            return "hikvision";
        } else if (url.contains("dahua") || url.contains("dh")) {
            return "dahua";
        } else if (url.contains("uniview") || url.contains("uni")) {
            return "uniview";
        } else if (url.contains("axis")) {
            return "axis";
        }
        
        // 通过IP地址段检测（可选，根据实际网络环境配置）
        try {
            URL urlObj = new URL(serviceUrl);
            String host = urlObj.getHost();
            
            // 可以根据实际部署的网络环境，通过IP段判断设备类型
            // 例如：海康设备通常部署在特定的IP段
            if (host.startsWith("192.168.1.") || host.startsWith("10.200.")) {
                // 这里可以根据实际情况调整IP段判断逻辑
                log.debug("根据IP段推测可能为海康设备: {}", host);
                return "hikvision";
            }
        } catch (Exception e) {
            log.debug("解析URL失败: {}", e.getMessage());
        }
        
        return null; // 未知设备类型，使用默认配置
    }
    
    /**
     * 根据设备信息检测设备类型
     * 
     * @param device 设备信息
     * @return 设备类型
     */
    private String detectDeviceTypeByDevice(OnvifDevice device) {
        // 优先根据制造商信息判断
        if (StrUtil.isNotEmpty(device.getManufacturer())) {
            String manufacturer = device.getManufacturer().toLowerCase();
            if (manufacturer.contains("hikvision") || manufacturer.contains("hik")) {
                return "hikvision";
            } else if (manufacturer.contains("dahua") || manufacturer.contains("dh")) {
                return "dahua";
            } else if (manufacturer.contains("uniview") || manufacturer.contains("uni")) {
                return "uniview";
            } else if (manufacturer.contains("axis")) {
                return "axis";
            }
        }
        
        // 根据设备名称判断
        if (StrUtil.isNotEmpty(device.getName())) {
            String name = device.getName().toLowerCase();
            if (name.contains("hikvision") || name.contains("hik") || name.contains("ds-")) {
                return "hikvision";
            } else if (name.contains("dahua") || name.contains("dh")) {
                return "dahua";
            }
        }
        
        // 根据服务URL判断
        if (StrUtil.isNotEmpty(device.getServiceUrl())) {
            return detectDeviceType(device.getServiceUrl());
        }
        
        return null;
    }
    
    /**
     * 海康设备专用的SOAP请求处理方法
     */
    private String sendSoapRequestForHikvision(String serviceUrl, String soapRequest, String username, String password, DeviceConfigManager.DeviceConfig config) throws IOException {
        log.info("使用海康设备专用连接处理逻辑: {}", serviceUrl);
        
        // 获取最优先的认证策略（使用ONVIF标准WS-Security认证）
        AuthenticationStrategy bestStrategy = authenticationManager.getStrategies().stream()
            .sorted((a, b) -> Integer.compare(a.getPriority(), b.getPriority()))
            .findFirst()
            .orElse(null);
        
        if (bestStrategy == null) {
            throw new IOException("无可用的认证策略");
        }
        
        log.info("海康设备使用{}认证策略", bestStrategy.getAuthType());
        
        try {
            // 处理WS-Security认证
            if (bestStrategy instanceof WsSecurityAuthStrategy) {
                WsSecurityAuthStrategy wsSecurityStrategy = (WsSecurityAuthStrategy) bestStrategy;
                String wsSecurityHeader = wsSecurityStrategy.buildWsSecurityHeader(username, password);
                if (StrUtil.isNotEmpty(wsSecurityHeader)) {
                    // 替换空的soap:Header为WS-Security头
                    String finalSoapRequest = soapRequest.replace("<soap:Header/>", wsSecurityHeader)
                                                        .replace("<soap:Header></soap:Header>", wsSecurityHeader);
                    return hikvisionConnectionManager.sendSoapRequest(serviceUrl, finalSoapRequest, null);
                }
                // 如果WS-Security头构建失败，使用原始请求
                return hikvisionConnectionManager.sendSoapRequest(serviceUrl, soapRequest, null);
            } else {
                // 构建传统认证头
                String authHeader = bestStrategy.buildAuthHeader(username, password, serviceUrl, "POST");
                return hikvisionConnectionManager.sendSoapRequest(serviceUrl, soapRequest, authHeader);
            }
            
        } catch (IOException e) {
            log.error("海康设备连接失败: {}", e.getMessage());
            
            // 尝试备用认证策略
            List<AuthenticationStrategy> backupStrategies = authenticationManager.getStrategies().stream()
                .filter(s -> !s.equals(bestStrategy))
                .sorted(Comparator.comparingInt(AuthenticationStrategy::getPriority))
                .limit(2) // 最多尝试2个备用策略
                .collect(Collectors.toList());
            
            if (!backupStrategies.isEmpty()) {
                log.info("尝试使用备用认证策略");
                
                for (AuthenticationStrategy backupStrategy : backupStrategies) {
                    try {
                        log.info("尝试备用{}认证策略", backupStrategy.getAuthType());
                        if (backupStrategy instanceof WsSecurityAuthStrategy) {
                            WsSecurityAuthStrategy wsSecurityStrategy = (WsSecurityAuthStrategy) backupStrategy;
                            String wsSecurityHeader = wsSecurityStrategy.buildWsSecurityHeader(username, password);
                            if (StrUtil.isNotEmpty(wsSecurityHeader)) {
                                String finalSoapRequest = soapRequest.replace("<soap:Header/>", wsSecurityHeader)
                                                                    .replace("<soap:Header></soap:Header>", wsSecurityHeader);
                                return hikvisionConnectionManager.sendSoapRequest(serviceUrl, finalSoapRequest, null);
                            }
                        } else {
                            String authHeader = backupStrategy.buildAuthHeader(username, password, serviceUrl, "POST");
                            return hikvisionConnectionManager.sendSoapRequest(serviceUrl, soapRequest, authHeader);
                        }
                    } catch (IOException be) {
                        log.warn("备用{}认证策略失败: {}", backupStrategy.getAuthType(), be.getMessage());
                    }
                }
            }
            
            // 所有策略都失败，抛出原始异常
            throw e;
        }

    }
    
    /**
     * 使用新连接发送SOAP请求（避免连接复用问题）
     */
    private String sendSoapRequestWithFreshConnection(String serviceUrl, String soapRequest, String username, String password, AuthenticationStrategy strategy, DeviceConfigManager.DeviceConfig config) throws IOException {
        URL url = new URL(serviceUrl);
        HttpURLConnection connection = null;
        
        try {
            // 直接创建新连接，不使用连接池
            connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(config.getConnectionTimeout());
            connection.setReadTimeout(config.getReadTimeout());
            connection.setUseCaches(false);
            connection.setInstanceFollowRedirects(false);
            
            // 设置连接属性
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setDoInput(true);
            
            // 设置请求头
            connection.setRequestProperty("Content-Type", "application/soap+xml; charset=utf-8");
            connection.setRequestProperty("SOAPAction", "");
            connection.setRequestProperty("Connection", "close"); // 强制关闭连接，避免复用
            
            // 使用认证策略设置认证头
            String authHeader = strategy.buildAuthHeader(username, password, serviceUrl, "POST");
            if (StrUtil.isNotEmpty(authHeader)) {
                connection.setRequestProperty("Authorization", authHeader);
                log.debug("已设置{}认证: username={}", strategy.getAuthType(), username);
            }
            
            // 发送请求
            try (OutputStream os = connection.getOutputStream()) {
                os.write(soapRequest.getBytes(StandardCharsets.UTF_8));
                os.flush();
            }
            
            // 读取响应
            int responseCode = connection.getResponseCode();
            log.debug("HTTP响应码: {}", responseCode);
            
            if (responseCode == HttpURLConnection.HTTP_OK) {
                try (InputStream is = connection.getInputStream();
                     BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
                    
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }
                    
                    String responseStr = response.toString();
                    log.debug("SOAP响应长度: {} 字符", responseStr.length());
                    return responseStr;
                }
            } else {
                // 读取错误响应
                try (InputStream es = connection.getErrorStream();
                     BufferedReader reader = new BufferedReader(new InputStreamReader(es != null ? es : connection.getInputStream(), StandardCharsets.UTF_8))) {
                    
                    StringBuilder errorResponse = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        errorResponse.append(line);
                    }
                    
                    String errorContent = errorResponse.toString();
                    log.error("HTTP错误响应 - 状态码: {}, 内容长度: {}", responseCode, errorContent.length());
                    log.debug("错误响应内容: {}", errorContent.substring(0, Math.min(errorContent.length(), 500)));
                    
                    // 分析具体的HTTP错误
                    String errorMessage = analyzeHttpError(responseCode, errorContent, serviceUrl);
                    throw new IOException(errorMessage);
                }
            }
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
    
    /**
     * 海康设备专用的媒体服务URL解析方法
     */
    private String parseHikvisionMediaServiceUrl(String response, OnvifDevice device) {
        try {
            log.info("开始海康设备专用媒体服务URL解析");
            
            // 海康设备可能使用的特殊XML格式
            String[] hikvisionPatterns = {
                // 尝试不同的命名空间组合
                "<tds:Media[^>]*>.*?<tds:XAddr[^>]*>(.*?)</tds:XAddr>",
                "<Media[^>]*>.*?<XAddr[^>]*>(.*?)</XAddr>",
                "Media.*?<XAddr[^>]*>(.*?)</XAddr>",
                "<.*?Media.*?>.*?<.*?XAddr.*?>(.*?)</.*?XAddr.*?>",
                // 更宽松的匹配
                "(?i)media.*?xaddr[^>]*>(.*?)</.*?xaddr",
                "(?i)<[^>]*media[^>]*>.*?<[^>]*xaddr[^>]*>(.*?)</[^>]*xaddr[^>]*>"
            };
            
            for (String pattern : hikvisionPatterns) {
                String url = extractValue(response, pattern, "");
                if (StrUtil.isNotEmpty(url)) {
                    log.info("海康设备使用模式 {} 提取到URL: {}", pattern, url);
                    
                    // 标准化URL格式
                    url = normalizeMediaServiceUrl(url, device);
                    
                    // 验证URL是否可用
                    if (testMediaServiceUrl(url, device)) {
                        return url;
                    }
                }
            }
            
            // 如果XML解析失败，尝试文本搜索
            log.info("XML模式解析失败，尝试文本搜索");
            String[] searchTerms = {"XAddr", "xaddr", "XADDR"};
            
            for (String term : searchTerms) {
                int index = response.indexOf(term);
                if (index > 0) {
                    // 查找URL的开始和结束
                    int start = response.indexOf(">", index) + 1;
                    int end = response.indexOf("<", start);
                    
                    if (start > 0 && end > start) {
                        String url = response.substring(start, end).trim();
                        if (url.startsWith("http")) {
                            log.info("文本搜索找到URL: {}", url);
                            url = normalizeMediaServiceUrl(url, device);
                            if (testMediaServiceUrl(url, device)) {
                                return url;
                            }
                        }
                    }
                }
            }
            
            log.warn("海康设备专用解析未找到有效的媒体服务URL");
            return null;
            
        } catch (Exception e) {
            log.error("海康设备媒体服务URL解析异常: {}", e.getMessage(), e);
            return null;
        }
    }
    
    /**
     * 标准化媒体服务URL格式
     */
    private String normalizeMediaServiceUrl(String url, OnvifDevice device) {
        if (StrUtil.isEmpty(url)) {
            return url;
        }
        
        // 移除可能的XML转义字符
        url = url.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">");
        
        // 检查是否需要添加端口
        if (!url.matches(".*://[^/]+:\\d+.*")) {
            String protocol = url.startsWith("https") ? "https" : "http";
            String path = url.substring(url.indexOf("/", 8)); // 跳过协议部分
            url = protocol + "://" + device.getIpAddress() + ":" + device.getPort() + path;
        }
        
        return url;
    }
    
    /**
     * 测试媒体服务URL是否可用（海康设备专用）
     */
    private boolean testMediaServiceUrl(String mediaServiceUrl, OnvifDevice device) {
        try {
            String testRequest = buildGetProfilesRequest();
            
            // 对海康设备使用专用的连接处理
            String deviceType = detectDeviceTypeByDevice(device);
            if ("hikvision".equals(deviceType)) {
                DeviceConfigManager.DeviceConfig config = deviceConfigManager.getDeviceConfig(deviceType);
                String response = sendSoapRequestForHikvision(mediaServiceUrl, testRequest, device.getUsername(), device.getPassword(), config);
                return StrUtil.isNotEmpty(response) && !response.contains("<soap:Fault>") && !response.contains("<env:Fault>");
            } else {
                String response = sendSoapRequest(mediaServiceUrl, testRequest, device.getUsername(), device.getPassword());
                return StrUtil.isNotEmpty(response) && !response.contains("<soap:Fault>") && !response.contains("<env:Fault>");
            }
        } catch (Exception e) {
            log.debug("测试媒体服务URL失败: {} - {}", mediaServiceUrl, e.getMessage());
            return false;
        }
    }
    
    /**
     * 测试服务URL是否可用
     * 
     * @param serviceUrl 服务URL
     * @param username 用户名
     * @param password 密码
     * @return 是否可用
     */
    private boolean testServiceUrl(String serviceUrl, String username, String password) {
        try {
            String testRequest = buildGetDeviceInformationRequest();
            String response = sendSoapRequest(serviceUrl, testRequest, username, password);
            return StrUtil.isNotEmpty(response) && !response.contains("<soap:Fault>") && !response.contains("<env:Fault>");
        } catch (Exception e) {
            log.debug("测试服务URL失败: {} - {}", serviceUrl, e.getMessage());
            return false;
        }
    }
}